12 Behavioral Patterns

Behavioral Patterns

Objective

  1. 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:

  1. Promote reusability: By using design patterns, developers can apply tried-and-true approaches to solve problems more efficiently without reinventing the wheel.
  2. Enhance code maintainability: Well-structured solutions simplify future modifications and reduce the risk of bugs when making changes.
  3. Improve communication: Design patterns provide a common vocabulary for developers, making it easier to discuss and share ideas.
  4. Facilitate scalability: Patterns often enable systems to be designed with future growth and additional functionality in mind.
  5. 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:

  1. Creational patterns: Focus on the process of object creation.
  2. Structural patterns: Deal with object composition and the relationships between entities to create larger, more complex structures.
  3. 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:

  1. Reorganizing class structures: Modifying class hierarchies or relationships (e.g., extracting a superclass, merging classes) to make the design clearer and reduce duplication.
  2. Improving method cohesion: Splitting or merging methods, adjusting their interactions in sequence diagrams to enhance code readability and reduce complexity.
  3. Renaming elements: Updating class, attribute, or method names in both the code and UML diagrams to make them more meaningful.
  4. Encapsulation adjustments: Modifying access controls or moving methods between classes while updating diagrams to match.
  5. 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

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.vpp
      • chain.pat
      • iterator.pat
      • observer.pat
  • Resources