DEV Community

daniellupaca
daniellupaca

Posted on

Behavioral patterns show code real world examples

Autores: Ronal Lupaca Mamani, Aaron Paco Ramos, Carlos Escobar Rejas, Edward Apaza Mamani

Abril 2024

Resumen

Este artículo explora los fundamentos y aplicaciones prácticas de los patrones de diseño de comportamiento en el desarrollo de software. Se analizan cuatro patrones clave: Chain of Responsibility, Command, Observer y Strategy, cada uno con su propósito y beneficios específicos. Se proporcionan ejemplos concretos de cómo estos patrones pueden ser implementados en sistemas reales, junto con analogías que ayudan a comprender su funcionamiento. Este análisis ofrece a los desarrolladores una comprensión sólida de cómo utilizar efectivamente los patrones de diseño de comportamiento para crear sistemas flexibles, modulares y fáciles de mantener.

Abstract

This article explores the fundamentals and practical applications of behavioral design patterns in software development. Four key patterns are analyzed: Chain of Responsibility, Command, Observer, and Strategy, each with its specific purpose and benefits. Concrete examples of how these patterns can be implemented in real-world systems are provided, along with analogies that aid in understanding their operation. This analysis offers developers a solid understanding of how to effectively utilize behavioral design patterns to create flexible, modular, and easily maintainable systems.

1. Introduction

In the world of software development, design patterns are essential tools for tackling common problems effectively. Among these solutions, behavior patterns stand out for their focus on how objects interact and communicate with each other during program execution.
In this article, we dive into the fascinating world of behavioral patterns in programming. We'll explain how these patterns help define and manage application behavior, promoting more flexible and maintainable code.

2. Desarrollo

2.1 Chain of Responsibility

This pattern allows the handling process to be flexible and extensible, making it easy to add new handlers or modify existing ones without altering other system components. Each handler is independent and the request travels through the chain until it is processed or reaches the end of the chain without being served.

Practical Application of the Chain of Responsibility Pattern:

In an online ordering system, access is required to be restricted so that only authenticated users can generate orders, while users with administrative permissions must have full access. Multiple sequential checks such as user authentication, request data validation, filtering of repeated requests from the same IP address, and result caching are performed to improve performance. However, the code becomes cluttered and difficult to maintain as more features are added, resulting in code duplication and difficulties in reusing checks on other system components.

Practical Solution of the Employer Chain of Responsibility:

The Chain of Responsibility design pattern is implemented, which transforms each check into a self-contained object called a handler. Each handler has a unique method for performing the check, and can decide to pass the request to the next link in the chain or stop processing. The handlers are linked in a chain and process the request in sequence. This allows code to be decoupled and makes it easier to add and reuse checks. The solution can also follow a variant in which a single handler processes the request or none at all, depending on their ability to handle it, which is common in GUI events.

Real-life analogy:

The process of seeking technical support to resolve issues with new hardware on your computer mirrors the Chain of Responsibility pattern. While you're trying to boot multiple operating systems, Windows adapts automatically, but Linux has problems. After going through several levels of operators, finally an expert engineer provides a specific solution: download and install the appropriate drivers for the hardware on Linux, which solves the problem and fills you with joy.

2.2 Command

Command is a design pattern that converts a request into a separate object that contains all the information needed for its execution. This makes it possible to parameterize methods with different requests, delay or queue the execution of a request, and handle operations that cannot be performed immediately.

Practical Application of the Command Pattern::

The task of implementing different clicking behaviors on the buttons on a toolbar in a text editing application becomes complicated and error-prone. The initial solution of creating numerous subclasses to handle these behaviors results in code that is difficult to maintain and error-prone. In addition, code duplication becomes a problem when similar operations need to be implemented in different parts of the application.

Practical Solution of the Command Pattern:

The Command pattern proposes a more elegant solution by separating the business logic from the user interface. It is suggested to create command classes that encapsulate the different operations that buttons can perform. These commands are executed by the buttons without the latter needing to know the details of the implementation of each command. This reduces the coupling between the user interface and business logic, making it easier to manage behaviors and avoid code duplication.

Real-life analogy:

Imagine you're in a restaurant after a long walk. You sit comfortably and decide to place your order with the waiter. This friendly employee takes note of your order on a piece of paper and takes it to the kitchen, where the chef will take care of preparing it. In the meantime, you can relax, knowing that your application is underway. Once the chef is done, the waiter checks that everything is as you requested it and then serves you the food at your table. This process of taking the order, sending it to the kitchen, and serving it at your table reflects how the Command pattern organizes actions into an orderly system, making it easier for both the customer and the chef.

2.3 Iterator

Iterator is a behavioral design pattern that allows you to traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.).

2.4 Mediator

Mediator is a behavioral design pattern that allows you to reduce chaotic dependencies between objects. The pattern restricts direct communications between objects, forcing them to collaborate only through a mediator object.

2.5 Memento

Memento is a behavioral design pattern that allows you to save and restore the previous state of an object without revealing the details of its implementation.

2.6 Observer

The Observer pattern, also known as the Observer, Publish-Subscribe, or Event-Subscriber, is a behavioral design pattern that allows you to define a subscription mechanism to notify multiple objects about any event that happens to another object they are observing.

Practical Application of the Observer Pattern:

Let's imagine a situation where you have an inventory management system for an online store. You have two types of items: the "Inventory", which keeps the stock of products available, and the "Customers", which want to be notified when a specific product is available again in the inventory.

Solution Proposed by the Observer Employer:

Inventory (Notifier): This object maintains a list of subscribers interested in receiving notifications about changes in product stock. It has methods for customers to subscribe and unsubscribe, as well as to notify customers when a specific product becomes available again.
Customer (Subscriber): These objects represent customers interested in receiving notifications about the availability of specific products. They implement an update method that is called when they receive a notification from the inventory.

Real-World Analogy:

An everyday example that illustrates the Observer pattern is the subscription system for magazines or newspapers. When you subscribe to a magazine, you no longer need to go to the store to check if the next issue is available. Instead, the notifier (the journal's publisher) sends new issues directly to your mailbox right after publication, or even before. Subscribers can unsubscribe at any time if they no longer wish to receive the new numbers.

2.7 State

The State pattern, also known as the State, is a behavior design pattern that allows an object to alter its behavior when its internal state changes. This behavior change is similar to changing classes, although the object remains of the same class.

Practical Application of the State Pattern:

Imagine you're developing an electronic device management system, and one of the main objects is a smartphone. This smartphone can have different statuses, such as "Unlocked", "Locked", and "Low Battery". Each state defines a specific set of behaviors for the device's buttons and switches.

Proposed Solution by the State Pattern:

The State pattern suggests creating new classes for each possible state and extracting all the state-specific behavior within those classes. Instead of having conditionals in a single class, the original object stores a reference to the current state and delegates state-related work to that object.

Real-World Analogy:

An everyday example that illustrates the State pattern is the behavior of buttons and switches on a smartphone. Depending on the status of the device (unlocked, locked, low battery, etc.), the buttons perform different functions.

2.8 Strategy

It is used to define a family of algorithms, encapsulating each one and making it interchangeable. This allows the algorithm to vary regardless of the customers using it. In short, the strategy makes it easy to dynamically change the behavior of an object during program execution without modifying its structure.

Practical Application of the Strategy:

Let's say you're developing a payment processing system for an e-commerce website. This system should be able to handle different payment methods, such as credit cards, PayPal, and bank transfers. This is where the Strategy pattern comes into play.

Solution proposed by Strategy:

An online payment processing system was implemented using the Strategy design pattern. Separate classes were created for each payment method (credit card, PayPal, bank transfer), each with a specific process_payment(amount) method. A context was then developed that uses one of these classes as needed, thus allowing the payment method to be dynamically changed without modifying the structure of the systems.

2.9 Strategy

Template Method is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but allows subclasses to overwrite algorithm steps without changing their structure.

Practical Application of the Template Method:

Let's say you're developing a financial reporting system for a business. Each type of report (e.g., balance sheet report, profit and loss report) follows a similar structure but with variations in the specific details.

Solution proposed by Template Method:

An abstract class is defined that contains a template method that defines the overall structure of the report and calls abstract methods to handle the specific details of each report.

2.10 Visitor

Visitor is a behavioral design pattern that allows you to separate algorithms from the objects they operate on.

Practical Application of the Visitor:

Let's say you're developing a document processing system that can handle different types of documents, such as text files, PDFs, and spreadsheets. We want to perform specific operations on each document type without modifying existing document types.

Solution proposed by Visitor:

Implement the Visitor design pattern to perform specific operations on different document types without modifying existing document types.

3. Recomendaciones

  1. Before applying design patterns, make sure you understand fundamental concepts such as encapsulation, inheritance, composition, and separation of concerns. This will help you better understand how and when to apply patterns effectively in your code.
  2. Familiarize yourself with a variety of design patterns, including behavioral, creational, and structural. Each type of pattern has its own purpose and application, so it's important to understand how and when to use them properly.
  3. Regardless of whether you're using design patterns or not, it's important to write clean, modular code.
  4. Not all problems require the application of a design pattern. Before applying a pattern, evaluate whether it's really the most appropriate solution for the problem you're trying to solve.

4. Conclusiones

  1. Behavioral design patterns, such as Chain of Responsibility, Command, Observer, and Strategy, allow systems to be more flexible and extensible by decoupling components and making it easier to add or modify functionality without affecting other parts of the system. This is achieved by separating responsibilities and promoting code reuse.
  2. Implementing these patterns promotes a more organized and maintainable code structure by separating concerns and avoiding code duplication.
  3. Patterns such as State and Strategy allow systems to be more adaptable to changes in requirements or the environment by making it easier to dynamically modify system behavior during program execution By applying these behavioral design patterns, a more modular and scalable architecture is promoted, making it easier to maintain and expand the system as it evolves over time.

Referencias

  1. Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). "Design Patterns: Elements of Reusable Object-Oriented Software". Addison-Wesley.
  2. Freeman, E., Robson, E., Bates, B., & Sierra, K. (2004). "Head First Design Patterns". O'Reilly Media.
  3. Shalloway, A., Trott, J. R., & Trott, J. (2019). "Design Patterns Explained: A New Perspective on Object-Oriented Design". Addison-Wesley.
  4. Freeman, E., Freeman, E., & Sierra, K. (2006). "Head First Object-Oriented Analysis and Design". O'Reilly Media.

Top comments (0)