A conversational assistant built with Dialogflow CX to teach the Strategy, Composite, and Observer design patterns.
đŚ Coo coo! Iâm Pattern Pigeonâyour feathered friend here to help you understand software architecture patterns like Strategy, Composite, and Observer. I might ruffle in a few pigeon facts too. What are you curious about today?
âPigeonâ by Raghunath Thirumalaisamy licensed đ đ Ż CC BY 2.0Creative Commons Attribution 2.0 Generic
đŚ Pattern Pigeon is a deterministicAll responses are generated by the agent, without generative input from a large language model (LLM). However, Flows use language models for understanding end-user intention, which may not be completely deterministic. conversational assistant built with Google Cloudâs Dialogflow CX to teach the Strategy, Composite, and Observer design patterns.
The Strategy pattern in a behavioral design pattern that defines a family of algorithms, encapsulates each one in a separate class, and makes them interchangeable. This allows the behavior of a class to be selected at runtime without modifying its code.
A common example is a navigation app. Suppose we have a class called Navigator
. Instead of implementing a large buildRoute(A, B)
that handles every routing logic internally, we delegate the route calculation to separate strategy classesâlike DrivingRouteStrategy
, WalkingRouteStrategy
, or CyclingRouteStrategy
. The Navigator
simply delegates work to the current strategy, making it easy to switch behaviors without rewriting the logic.
Another example is a fighting video game, where each character has a unique moveset. Rather than hardcoding each fighterâs behavior into one giant Fighter
class, we can use the Strategy pattern to separate the moveset logic.
For example, we might define a MovesetStrategy
interface with methods like lightAttack()
, heavyAttack()
, and specialMove()
. Then we create different strategy classes like KarateMoveset
, BoxingMoveset
, or MagicMoveset
, each implementing those methods in their own way.
The Fighter
class holds a reference to a MovesetStrategy
, allowing you to assign or change fighting styles dynamicallyâlike equipping a new skill set mid-game or supporting custom characters.
The Strategy pattern encourages software maintainability and extensibility by decoupling algorithms from the classes that use them. This makes software easier to test, more flexible, and open to extension.
While the Strategy pattern offers flexibility, there are also a few trade-offs. There are more classes to manage, increasing code complexity if not organized well. For simple behavior, it might be unnecessarily complicate the design.
The Composite pattern is a structural design pattern used to represent tree-like hierarchies of objects. It allows individual objects (leaves) and groups of objects (composites) to be treated uniformly through a shared interface.
In this pattern, there is a Component interface that both individual elements and composite groups implement. A Composite class contains child componentsâboth leaves and other compositesâand delegates all world to them.
Imagine an image editor that defines a Graphic
interface. The smallest drawable element could be a Dot
, which implements this interface. More complex shapes like Circle
or CompoundGraphic
(which may group multiple Graphic
objects) also implement the same interface. This way, the editor can handle single elements and groups of elements in the same way, such as drawing, moving, or deleting them.
The SEC is a great real-world example of the Composite pattern. The conference as a whole can be thought of as a composite object that contains multiple components. These components can be either teams (composite) or players (leaves).
Each player represents a leaf node, an individual unit that does not contain other components. A team represents a composite node containing multiple players and can perform operations like listing player stats, or calculating team rankings. The entire conference could be a composite that contains all teams. Operations like âprint all team rostersâ or âcompute average team performanceâ can be delegated down through the structure.
Because both teams and players adhere to a common interface (such as displayStats()
or getRanking()
), clients can treat them uniformly. Whether the component is a single player or an entire team, the operation is invoked the same way.
The Composite pattern is useful when you need to work with tree-like structures where individual objects and groups of objects should be treated the same way. The uniformity of the components and compositions make things simple for clients. It also makes it really easy to scale the tree structure by adding new leaves without changing existing code. It also decouples the client from differentiating between dealing with a single object or a composite, making things simpler!
While the Composite pattern is powerful, there are some disadvantages. It can be complex to design, as a component that works for both simple and composite elements can be challenging to make. Itâs also hard to restrict behavior because all components share the same interface.
The Observer Pattern is a behavioral design pattern that establishes a subscription system, where a âPublisherâ (or âSubjectâ) can notify multiple âObserversâ of events or changes in state.
One is the multiplayer game PokĂŠmon Go. Imagine thereâs a wild PigeonâŚerrrâŚa wild PidgeotâŚ, and four players are nearby trying to catch it. When one player successfully captures it, the game needs to notify the other players that the Pidgeot is no longer available. The game server (the âPublisherâ) sends updates to all connected clients (the âObserversâ) to reflect this change, ensuring consistency across the game world.
Another is a weather app. When an event occursâlike a temperature change or an updated rain forecastâthe server (acting as the âPublisherâ) sends out the new data. All subscribed client apps (the âObserversâ) receive the update and refresh the information displayed to the user.
In this pattern, the Publisher/Subject maintains a list of all the registered subscribers, then when an event occurs that requires a notification, calls a method to each subscriber to let them know that the event occurred. Thus, the observer is the class that responds to the event.
There are many advantages. The subject and the observer do not require tight coupling, which makes their connection very flexible and extensible. You can add new observers very easily without modifying the subject. It is also a very efficient way to get a message out from one server to many clients in a real-time scenario.
With many observers, there could be performance implications of notifying all of them if the operation is costly or takes a long time. Also, the pattern relies on the reliability and format of the notification from the subject, so any updates to the publisher requires updates to the observers.
Below is a sample conversation that demonstrates how the bot interacts.
Hi!
đŚ Hey there! Iâm Pattern Pigeon. Iâll help you learn about common software design patterns like Strategy, Composite, and Observerâstep by step. I might also chirp in with some pigeon trivia. What would you like to start with?
Can I learn about the Observer pattern?
Coo coo! Observer it is! What would you like to learn about?
What do you know?
About the observer pattern, why not ask for some good examples? I can answer the difference between the Publisher/Subject and the Observer, why use the Observer pattern, and any disadvantages!
First, what is the observer pattern?
Alright, hereâs the deal, coo-coo! The Observer Pattern is a behavioral design pattern that establishes a structured subscription mechanism, wherein a âPublisherâ (or âSubjectâ) is capable of notifying multiple âObserversâ about specific events or changes in state as they occur. Just like that, when the crumbs hit the ground, everybirdyâs in the loop, ya feel me?
Okay, letâs do something else
Enough Observer for now, back to the coop! What to dive into next, Composite, Observer, or Strategy?
Before we continue on the next topic, could you tell me a fun fact about pigeons?
Oooh, ooh, ooh! Hereâs a good one. Did you know that pigeons were âprobably one of the first animals to be domesticated by humans?â According to Atlas Obscura, images and carvings of the pigeons first appear in the Middle East and North Africa around 3,000 years ago (https://www.atlasobscura.com/articles/ancient-egypt-pigeon-problem). Want to hear another?
Nah.
Enough facts for one day, I get it. Letâs fly back to the coop! What to dive into next, Composite, Observer, or Strategy?
[End]
You can find the source code repository for Pattern Pigeon at thatrobotdev/Pattern-Pigeon.
Is our feathered friend not squacking right? Noticed a typo? Want to add new content? Well youâre in luck! Follow these instructions to warp Pattern Pigeon into your Dialogflow CX dashboard so you can contribute changes!
Follow the instructions from Google Cloud to set up GitHub integration:
The pigeon photo used was taken by Muhammad Mahdi Karim/https://micro2macro.net. The image above is licensed under the đŻ Free Art License. (Learn more). This project is not endorsed or connected to the Apache Software Foundation.
This project is licensed under the Apache License, Version 2.0, which you can read here.