Los patrones de diseño son soluciones reutilizables a problemas comunes en el diseño de software. En Delphi, como en otros lenguajes de programación orientados a objetos, los patrones de diseño ayudan a crear código más robusto, mantenible y escalable. En esta sección, exploraremos algunos de los patrones de diseño más utilizados y cómo implementarlos en Delphi.

  1. Introducción a los Patrones de Diseño

¿Qué son los Patrones de Diseño?

  • Definición: Un patrón de diseño es una solución general y reutilizable para un problema común en un contexto dado.
  • Categorías: Los patrones de diseño se dividen en tres categorías principales:
    • Patrones Creacionales: Se enfocan en la creación de objetos.
    • Patrones Estructurales: Se enfocan en la composición de clases y objetos.
    • Patrones de Comportamiento: Se enfocan en la interacción y responsabilidad entre objetos.

Beneficios de Usar Patrones de Diseño

  • Reutilización de Código: Facilitan la reutilización de soluciones probadas.
  • Mantenibilidad: Mejoran la estructura y claridad del código.
  • Flexibilidad: Permiten crear sistemas más flexibles y adaptables a cambios.

  1. Patrones Creacionales

2.1. Singleton

El patrón Singleton asegura que una clase tenga una única instancia y proporciona un punto de acceso global a ella.

Implementación en Delphi

type
  TSingleton = class
  private
    class var FInstance: TSingleton;
    constructor Create; // Constructor privado
  public
    class function GetInstance: TSingleton;
  end;

constructor TSingleton.Create;
begin
  inherited;
  // Inicialización de la instancia
end;

class function TSingleton.GetInstance: TSingleton;
begin
  if FInstance = nil then
    FInstance := TSingleton.Create;
  Result := FInstance;
end;

Explicación

  • Constructor Privado: Evita la creación de instancias fuera de la clase.
  • Método GetInstance: Crea y devuelve la única instancia de la clase.

2.2. Factory Method

El patrón Factory Method define una interfaz para crear un objeto, pero permite a las subclases alterar el tipo de objetos que se crearán.

Implementación en Delphi

type
  TProduct = class
    procedure Operation; virtual; abstract;
  end;

  TConcreteProductA = class(TProduct)
    procedure Operation; override;
  end;

  TConcreteProductB = class(TProduct)
    procedure Operation; override;
  end;

  TCreator = class
    function FactoryMethod: TProduct; virtual; abstract;
  end;

  TConcreteCreatorA = class(TCreator)
    function FactoryMethod: TProduct; override;
  end;

  TConcreteCreatorB = class(TCreator)
    function FactoryMethod: TProduct; override;
  end;

procedure TConcreteProductA.Operation;
begin
  // Implementación específica de A
end;

procedure TConcreteProductB.Operation;
begin
  // Implementación específica de B
end;

function TConcreteCreatorA.FactoryMethod: TProduct;
begin
  Result := TConcreteProductA.Create;
end;

function TConcreteCreatorB.FactoryMethod: TProduct;
begin
  Result := TConcreteProductB.Create;
end;

Explicación

  • Clase Abstracta TProduct: Define la interfaz de los productos.
  • Subclases TConcreteProductA y TConcreteProductB: Implementan la interfaz de productos.
  • Clase Abstracta TCreator: Define el método FactoryMethod.
  • Subclases TConcreteCreatorA y TConcreteCreatorB: Implementan el método FactoryMethod para crear instancias específicas.

  1. Patrones Estructurales

3.1. Adapter

El patrón Adapter permite que clases con interfaces incompatibles trabajen juntas.

Implementación en Delphi

type
  TTarget = class
    procedure Request; virtual;
  end;

  TAdaptee = class
    procedure SpecificRequest;
  end;

  TAdapter = class(TTarget)
  private
    FAdaptee: TAdaptee;
  public
    constructor Create(AAdaptee: TAdaptee);
    procedure Request; override;
  end;

procedure TTarget.Request;
begin
  // Implementación por defecto
end;

procedure TAdaptee.SpecificRequest;
begin
  // Implementación específica
end;

constructor TAdapter.Create(AAdaptee: TAdaptee);
begin
  FAdaptee := AAdaptee;
end;

procedure TAdapter.Request;
begin
  FAdaptee.SpecificRequest;
end;

Explicación

  • Clase TTarget: Define la interfaz que se espera.
  • Clase TAdaptee: Tiene una interfaz incompatible que necesita ser adaptada.
  • Clase TAdapter: Adapta la interfaz de TAdaptee a la interfaz de TTarget.

  1. Patrones de Comportamiento

4.1. Observer

El patrón Observer define una dependencia uno a muchos entre objetos, de manera que cuando un objeto cambia de estado, todos sus dependientes son notificados y actualizados automáticamente.

Implementación en Delphi

type
  IObserver = interface
    procedure Update;
  end;

  TSubject = class
  private
    FObservers: TList<IObserver>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Attach(AObserver: IObserver);
    procedure Detach(AObserver: IObserver);
    procedure Notify;
  end;

  TConcreteObserver = class(TInterfacedObject, IObserver)
  public
    procedure Update;
  end;

constructor TSubject.Create;
begin
  FObservers := TList<IObserver>.Create;
end;

destructor TSubject.Destroy;
begin
  FObservers.Free;
  inherited;
end;

procedure TSubject.Attach(AObserver: IObserver);
begin
  FObservers.Add(AObserver);
end;

procedure TSubject.Detach(AObserver: IObserver);
begin
  FObservers.Remove(AObserver);
end;

procedure TSubject.Notify;
var
  Observer: IObserver;
begin
  for Observer in FObservers do
    Observer.Update;
end;

procedure TConcreteObserver.Update;
begin
  // Implementación de la actualización
end;

Explicación

  • Interfaz IObserver: Define el método Update que deben implementar los observadores.
  • Clase TSubject: Mantiene una lista de observadores y los notifica de los cambios.
  • Clase TConcreteObserver: Implementa la interfaz IObserver y define la lógica de actualización.

Ejercicios Prácticos

Ejercicio 1: Implementar el Patrón Singleton

  1. Crea una clase TLogger que implemente el patrón Singleton.
  2. La clase debe tener un método Log que acepte un mensaje y lo imprima en la consola.

Solución

type
  TLogger = class
  private
    class var FInstance: TLogger;
    constructor Create;
  public
    class function GetInstance: TLogger;
    procedure Log(const Msg: string);
  end;

constructor TLogger.Create;
begin
  inherited;
end;

class function TLogger.GetInstance: TLogger;
begin
  if FInstance = nil then
    FInstance := TLogger.Create;
  Result := FInstance;
end;

procedure TLogger.Log(const Msg: string);
begin
  Writeln(Msg);
end;

// Uso
var
  Logger: TLogger;
begin
  Logger := TLogger.GetInstance;
  Logger.Log('Este es un mensaje de log.');
end;

Ejercicio 2: Implementar el Patrón Observer

  1. Crea una clase TStock que actúe como el sujeto.
  2. Crea una clase TInvestor que actúe como el observador.
  3. Implementa la lógica para que los inversores sean notificados cuando el precio de la acción cambie.

Solución

type
  IInvestor = interface
    procedure Update(Stock: TObject);
  end;

  TStock = class
  private
    FPrice: Double;
    FInvestors: TList<IInvestor>;
    procedure SetPrice(const Value: Double);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Attach(Investor: IInvestor);
    procedure Detach(Investor: IInvestor);
    procedure Notify;
    property Price: Double read FPrice write SetPrice;
  end;

  TInvestor = class(TInterfacedObject, IInvestor)
  public
    procedure Update(Stock: TObject);
  end;

constructor TStock.Create;
begin
  FInvestors := TList<IInvestor>.Create;
end;

destructor TStock.Destroy;
begin
  FInvestors.Free;
  inherited;
end;

procedure TStock.Attach(Investor: IInvestor);
begin
  FInvestors.Add(Investor);
end;

procedure TStock.Detach(Investor: IInvestor);
begin
  FInvestors.Remove(Investor);
end;

procedure TStock.Notify;
var
  Investor: IInvestor;
begin
  for Investor in FInvestors do
    Investor.Update(Self);
end;

procedure TStock.SetPrice(const Value: Double);
begin
  FPrice := Value;
  Notify;
end;

procedure TInvestor.Update(Stock: TObject);
begin
  Writeln('El precio de la acción ha cambiado a: ', TStock(Stock).Price:0:2);
end;

// Uso
var
  Stock: TStock;
  Investor1, Investor2: IInvestor;
begin
  Stock := TStock.Create;
  try
    Investor1 := TInvestor.Create;
    Investor2 := TInvestor.Create;
    Stock.Attach(Investor1);
    Stock.Attach(Investor2);
    Stock.Price := 100.0;
    Stock.Price := 120.5;
  finally
    Stock.Free;
  end;
end;

Conclusión

En esta sección, hemos explorado algunos de los patrones de diseño más comunes y cómo implementarlos en Delphi. Los patrones de diseño son herramientas poderosas que pueden mejorar significativamente la calidad y mantenibilidad de tu código. A medida que avances en tu carrera como desarrollador, te encontrarás con estos patrones una y otra vez, y comprender cómo y cuándo usarlos te convertirá en un programador más eficiente y efectivo.

En el próximo tema, profundizaremos en técnicas de refactorización para mejorar y optimizar el código existente.

Curso de Programación en Delphi/Object Pascal

Módulo 1: Introducción a Delphi/Object Pascal

Módulo 2: Estructuras de Control y Procedimientos

Módulo 3: Trabajando con Datos

Módulo 4: Programación Orientada a Objetos

Módulo 5: Características Avanzadas de Delphi

Módulo 6: Desarrollo de GUI con VCL y FMX

Módulo 7: Desarrollo Web y Móvil

Módulo 8: Mejores Prácticas y Patrones de Diseño

Módulo 9: Proyecto Final

© Copyright 2024. Todos los derechos reservados