Behavioral Patterns
Creational
Structural
Behavioral
- Chain of Responsibility (opens in a new tab)
- Command (opens in a new tab)
- Interpreter (opens in a new tab)
- Iterator (opens in a new tab)
- Mediator (opens in a new tab)
- Memento (opens in a new tab)
- Observer (opens in a new tab)
- State (opens in a new tab)
- Strategy (opens in a new tab)
- Template (opens in a new tab)
- Visitor (opens in a new tab)
Objective
- Behavioral design patterns.
Design Patterns
Design patterns are proven, reusable solutions to common problems that occur in software design. They represent best practices that experienced developers have refined over time, and they can be adapted to fit various situations. Each pattern provides a standard template for solving specific design challenges, which helps streamline the development process and improve code maintainability and scalability.
Design patterns offer several benefits:
- Promote reusability: By using design patterns, developers can apply tried-and-true approaches to solve problems more efficiently without reinventing the wheel.
- Enhance code maintainability: Well-structured solutions simplify future modifications and reduce the risk of bugs when making changes.
- Improve communication: Design patterns provide a common vocabulary for developers, making it easier to discuss and share ideas.
- Facilitate scalability: Patterns often enable systems to be designed with future growth and additional functionality in mind.
- Enforce best practices: Using patterns encourages developers to adhere to proven coding principles, which results in more robust and reliable software.
Design patterns are generally categorized into three main types:
- Creational patterns: Focus on the process of object creation.
- Structural patterns: Deal with object composition and the relationships between entities to create larger, more complex structures.
- Behavioral patterns: Concerned with the interaction and responsibility between objects.
By applying the appropriate design patterns, developers can create more maintainable, flexible, and efficient code that aligns with established best practices.
Code refactoring refers to the process of restructuring an existing codebase without changing its external behavior, while ensuring that the corresponding UML diagrams, such as class, sequence, or activity diagrams, are updated to reflect these changes. This practice helps improve the design, structure, and maintainability of the software.
Refactoring might involve:
- Reorganizing class structures: Modifying class hierarchies or relationships (e.g., extracting a superclass, merging classes) to make the design clearer and reduce duplication.
- Improving method cohesion: Splitting or merging methods, adjusting their interactions in sequence diagrams to enhance code readability and reduce complexity.
- Renaming elements: Updating class, attribute, or method names in both the code and UML diagrams to make them more meaningful.
- Encapsulation adjustments: Modifying access controls or moving methods between classes while updating diagrams to match.
- Simplifying interactions: Streamlining complex process flows represented in activity or sequence diagrams by refactoring logic.
Overall, the goal of code refactoring is to maintain consistent documentation and improve the system’s design quality without altering the end functionality.
“There are so many variations on the “there are only two hard problems in computer programming…” joke that I’m starting to suspect that programming isn’t actually very easy.” —Nat Pryce
Behavioral Patterns
- Chain of Responsibility: Sequential request handling
- Command: Request encapsulation
- Interpreter: Language grammar interpretation
- Iterator: Collection traversal
- Mediator: Decoupled communication
- Memento: State capture and restoration
- Observer: State change notification
- State: State-dependent behavior
- Strategy: Interchangeable algorithms
- Template: Algorithm skeleton
- Visitor: Operation separation
Chain of Responsibility
How can we pass requests along a chain of handlers until one processes the request? For example, a bank transaction may require multiple approvals.
- Multiple objects may handle a request
- Do not want sender to know which object handles the request
- Set of handlers can change dynamically
Command
How can we encapsulate a request as an object, allowing us to parameterize clients with different requests? For example, a menu item that may represent a command.
- Need to parameterize objects with operations
- Want to queue, log, or undo operations
- Support operations that can be undone
Interpreter
How can we interpret a language grammar? For example, a regular expression pattern.
- Need to interpret a language grammar
- Grammar is simple
- Efficiency is not a critical concern
Iterator
How can we access elements of a collection sequentially without exposing its underlying representation? For example, a list of items.
- Need to traverse a collection without exposing its structure
- Want to provide multiple traversal methods
- Collection structure may vary
Mediator
How can we reduce coupling between objects by making them communicate indirectly through a mediator object? For example, a chat room or a traffic control system.
- Many objects need to interact with each other
- Want to avoid objects referring to each other explicitly
- Object interactions are complex but well-defined
Memento
How can we capture and externalize an object’s internal state without violating encapsulation, so that the object can be restored to this state later? For example, implementing undo/redo in a text editor or saving game checkpoints.
- Need to maintain history of an object’s state
- Want to restore object to previous state
- Need to encapsulate state data outside the original object
- Do not want to expose internal implementation details
Observer
How can we define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified? For example, a stock price change or a weather station.
- One-to-many dependency between objects
- State changes in one object should trigger changes in other objects
- Do not want tight coupling between subject and observers
- Number of observers may vary
State
How can an object alter its behavior when its internal state changes? For example, a vending machine or a traffic light.
- Object behavior depends on its state
- State transitions are frequent
- State-specific behavior should be localized
Strategy
How can we define a family of algorithms, encapsulate each one, and make them interchangeable? For example, a sorting or hashing algorithm.
- Need different variants of an algorithm
- Algorithms should be interchangeable
- Want to avoid multiple conditionals
Template
How can we define the skeleton of an algorithm, deferring some steps to subclasses? For example, a document editor or a compiler.
- Several algorithms share a common structure
- Want to avoid code duplication
- Some steps of the algorithm are invariant
Visitor
How can we separate an algorithm from the objects on which it operates? For example, a document formatting or an expression evaluation.
- Need to perform operations on elements of an object structure
- Want to add new operations without changing the objects
- Operations have different implementations for different object types
Exercise 1
Design class diagrams that use at least three behavioral design patterns.
Explore the 10 SOLID + GRASP guide to learn more about the SOLID principles and GRASP patterns.
Structure
behavioral.vppchain.patiterator.patobserver.pat
Resources
- Visual Paradigm: Visual Paradigm tutorials (opens in a new tab), Using design pattern (opens in a new tab)
- Refactoring.Guru (opens in a new tab), SourceMaking (opens in a new tab), Patterns.dev (opens in a new tab)
- Gangs of Four (GoF) design patterns (opens in a new tab)
- GoF design patterns examples (opens in a new tab)
- Deceptive Patterns (opens in a new tab)
- Code refactoring using IntelliJ IDEA (opens in a new tab)
- Code refactoring (opens in a new tab), Principle of least astonishment (opens in a new tab), Law of Demeter (opens in a new tab), Separation of concerns (opens in a new tab), Aspect-oriented programming (opens in a new tab), POSA (opens in a new tab)