Skip to content

Aprende Patrones de Diseño

Vanskarner edited this page Jul 26, 2023 · 8 revisions

Los patrones de diseño se pueden definir como soluciones probadas a problemas comunes de diseño de software. Sin embargo, su utilidad va más allá, ya que también desempeñan un papel importante al transmitir la intención original de un programador a otro que retoma el trabajo en un sistema, facilitando la colaboración entre distintos programadores y asegurando una continuidad fluida.

Se podría considerar que el recurso inicial relacionado con los patrones de diseño es el artículo llamado Using Pattern Languages for OO Programs (1987) de Kent Beck y Ward Cunningham, presentado en el Taller de Programación Orientada a Objetos, Sistemas, Lenguajes y Aplicaciones (OOPSLA), donde se introdujeron varios patrones. Sin embargo, el recurso que popularizó los patrones de diseño es el libro Design Patterns: Elements of Reusable Object-Oriented Software (1994) de Gamma Erich, Helm Richard, Johnson Ralph y Vlissides John , y en este libro se presenta 23 patrones de diseño ordenados como se muestra a continuación:

graph TD;
    CreationalItems["- Abstract Factory
    - Builder
    - Factory Method
    - Prototype
    - Singleton"]
    StructuralItems["- Adapter
    - Bridge
    - Composite
    - Decorator
    - Facade
    - Flyweight
    - Proxy"]
    BehavioralItems["- Chain of Responsibility
    - Command
    - Interpreter
    - Iterator
    - Mediator
    - Memento
    - Observer
    - State
    - Strategy
    - Template Method
    - Visitor"]

    A[Design Patterns] --> B[Creational];
    A --> C[Structural];
    A --> D[Behavioral];

    B --> CreationalItems;
    C--> StructuralItems;
    D--> BehavioralItems;
Loading

Otros patrones

Otros recursos para aprender sobre patrones es el libro Patterns of Enterprise Application Architecture (2002) de Martin Fowler y el libro XUnit Test Patterns: Refactoring Test Code (2007) de Gerard Meszaros. En estos libros se encuentran algunos patrones que se mencionan en el libro "Clean Architecture: A Craftsman's Guide to Software Structure and Design" y a los cuales he adaptado su contenido solo un poco (casi nada) para relacionarlos con el tema en cuestión. Por ejemplo:

Humble Object

Patrón de diseño que separa el comportamiento difícil de probar (objeto humilde) del comportamiento fácil de probar (objeto inteligente), facilitando así las pruebas. Este patrón se encuentra en el libro XUnit Test Patterns: Refactoring Test Code, aunque el terminó "Humilde" tiene sus orígenes en el artículo de Michael Feathers llamado The Humble Dialog Box (2002).

flowchart LR
    DifficultComponentToTest["<font color=black>Difficult component
    to test"]
    Humble["<font color=black>Hard to test behavior
    (Humble)"]
    Smart["Easy to test behavior
    (Smart)"]

    DifficultComponentToTest --> Humble
    Humble -.-> Smart

    style DifficultComponentToTest fill:#CFE4FF
    style Humble fill:#CFE4FF 
Loading

El objeto humilde contiene el código difícil de probar debido generalmente a su dependencia con recursos externos. Este código difícil de probar normalmente se relaciona con:

  • Interacciones con servicios externos: Llamadas a servicios externos como bases de datos, APIs web u otros sistemas.
  • Dependencia de componentes externos: Librerías o frameworks.
  • Acceso a recursos de hardware o sistemas operativos: Operaciones directas en el hardware o interacción con el sistema operativo.
  • Código asíncrono: Si la asincronía no se maneja adecuadamente en las pruebas, genera incertidumbre.

El objeto inteligente contiene el código fácil de probar que se encuentra más cercano a la lógica de la aplicación. Este código fácil de probar se relaciona con los presentadores, casos de uso, y otros.

Este patrón separa la lógica dependiente de recursos externos en un componente, módulo o clase considerado como humilde que contendrá todo el código difícil de probar reducido a funciones puntuales; por otro lado, la lógica restante independiente de recursos externos residirá en un componente, módulo o clase considerado como inteligente que contendrá el comportamiento significativo de la aplicación.

Mapper

Patrón que configura una comunicación entre dos objetos independientes. Este patrón se encuentra en el libro Patterns of Enterprise Application Architecture.

flowchart LR
 Service
 Fee
 PaymentMapper[Payment Mapper]
 PaymentPackage["Payment Package"]

 PaymentMapper -.-> Service
 PaymentMapper -.-> Fee
 PaymentMapper -.-> PaymentPackage
Loading

Este patrón permite establecer comunicaciones entre dos subsistemas sin crear dependencias directas entre ellos. Un tipo de este mapper que probablemente hayas visto en varios proyectos es el siguiente:

Assembler Object

Es un mapeador entre el objeto de transferencia de datos (DTO) y los objetos de negocio; sin embargo, me enfocaré en la función central de este elemento, que es convertir un tipo a otro, ya que también se utiliza en otras partes del sistema. Es importante aclarar que aunque este tipo se llama "Assembler", muchos desarrolladores prefieren referirse a él simplemente como "Mapper":

classDiagram
 Mapper ..> DTO
 Mapper ..> BusinessObject

 class Mapper{
   + convert(BusinessObject): DTO
   + convert(DTO): BusinessObject
 }
 class DTO
 class BusinessObject
Loading

Data Mapper

Patrón que desplaza datos entre los objetos de negocio y la base de datos, manteniendo ambos elementos independientes y ajenos entre sí, así como también ajenos al propio Data Mapper. Este patrón se encuentra en el libro Patterns of Enterprise Application Architecture.

classDiagram
  direction LR
    Student <.. StudentDataMapper 
    StudentDataMapper ..> DatabaseApi

    class Student{
        - id: long
        - firstName: String
        - lastName: String
        - numberOfCredits: int
        + Student(long,String,String,int)
        + someBehavior(): boolean
        + getId(): long
        + getFirstName(): String
        + getLastName(): String
        + getNumberOfCredits(): int
    }
    class StudentDataMapper{
        + insert(Student): void
        + update(Student): void
        + find(long): Student
    }
    class DatabaseApi
Loading

Este patrón es un tipo de Mapper que además realiza operaciones en la base de datos, por lo que contiene el código de persistencia.

Repository

Patrón de diseño que proporciona una abstracción entre la lógica de negocio y las capas de mapeo de datos mediante el uso de una interfaz que se asemeja a una colección para acceder a los objetos de negocio. Este patrón se encuentra en el libro Patterns of Enterprise Application Architecture.

flowchart LR
Database[(Database)]
BusinessLogic[Business Logic]
Repository[Repository]
DataMapper[Data Mapper]
BusinessLogic <-- Persist / Recover  --> Repository
Repository -.-> DataMapper
DataMapper -.-> Database
Loading

En este patrón, el repositorio no contiene el código de persistencia de forma directa, ya que es otra capa de abstracción adicional.

Diferencias con Uncle Bob

Es común observar en la implementación de este patrón que se incluya una interfaz con un nombre similar a "SomeRepository". A esta interfaz también Uncle Bob la llama "Database Gateway" (por favor, note que no mencioné solo "Gateway") y proporciona la siguiente definición:

Estas puertas de enlace son interfaces polimórficas que contienen métodos para cada operación de creación, lectura, actualización o eliminación que puede realizar la aplicación en la base de datos.

- Robert C. Martin (Uncle Bob), Clean Architecture: A Craftsman’s Guide to Software Structure and Design, 2017

Diferencias con el Patrón Data Mapper

El repositorio se encuentra por encima de Data Mapper, pero en este caso actúa como otra capa de abstracción sobre la capa de mapeo para consultas complicadas o simplemente para un desacoplamiento mayor. Por lo tanto, el repositorio pueden contener o utilizar los propios Data Mappers.

Diferencias con el Patrón DAO

El patrón DAO es una abstracción de la fuente de datos que proporciona operaciones de datos sin exponer los detalles ligados a este y está más cercano a la fuente de datos. Por otro lado el repositorio actúa como una colección de objetos de negocio con capacidades de operaciones de datos y se encuentra a un nivel superior a DAO porque está más cerca de la lógica de negocio. Por lo tanto, el repositorio puede contener o utilizar a DAO para llevar a cabo sus operaciones.

La siguiente representación es mi intento de integrarlo con los otros conceptos:

flowchart LR
Database[(Data Source)]
BusinessLogic[Business Logic]
Repository[Repository]
DAO[Data Access Object]
BusinessLogic <-- Persist / Recover  --> Repository
Repository -.-> DAO
DAO -.-> Database
Loading

Si bien DAO puede parecer similar al Data Mapper, lo hace en el sentido de que ambos contienen el código de persistencia, pero se diferencian en que DAO no tiene dependencias con los objetos de negocio, mientras que Data Mapper sí las tiene. Asimismo, Martin Fowler hace un pequeño análisis del patrón DAO basándose en el libro Core J2EE Patterns: Best Practices and Design Strategies (2001) de Deepak Alur, John Crupi y Dan Malks, como una puerta de enlace de datos de tabla, pero no tiene claro si siempre se basa en una tabla y además especifica que DAO tiene otro significado en el mundo de Microsoft de aquel tiempo.

Clone this wiki locally