Els patrons de disseny són solucions provades a problemes comuns en el desenvolupament de programari. Aquests patrons ajuden a crear codi més organitzat, reutilitzable i mantenible. En aquest tema, explorarem alguns dels patrons de disseny més comuns i com implementar-los en Delphi/Object Pascal.

Introducció als Patrons de Disseny

Què són els Patrons de Disseny?

  • Definició: Un patró de disseny és una solució general reutilitzable per a un problema comú en un context de disseny de programari.
  • Categories: Els patrons de disseny es classifiquen en tres categories principals:
    • Patrons Creacionals: Tracten amb la creació d'objectes.
    • Patrons Estructurals: Tracten amb la composició d'objectes.
    • Patrons de Comportament: Tracten amb la interacció entre objectes.

Beneficis dels Patrons de Disseny

  • Reutilització del Codi: Faciliten la reutilització de solucions provades.
  • Mantenibilitat: Milloren la mantenibilitat del codi.
  • Comunicació: Proporcionen un llenguatge comú per als desenvolupadors.

Patrons Creacionals

Patró Singleton

El patró Singleton assegura que una classe només tingui una instància i proporciona un punt d'accés global a aquesta instància.

Implementació en Delphi

type
  TSingleton = class
  private
    class var FInstance: TSingleton;
    constructor Create; reintroduce;
  public
    class function GetInstance: TSingleton;
  end;

constructor TSingleton.Create;
begin
  inherited Create;
  // Inicialització de l'objecte
end;

class function TSingleton.GetInstance: TSingleton;
begin
  if not Assigned(FInstance) then
    FInstance := TSingleton.Create;
  Result := FInstance;
end;

Explicació

  • Constructor Privat: El constructor és privat per evitar la creació d'instàncies des de fora de la classe.
  • Mètode Estàtic: GetInstance és un mètode estàtic que crea i retorna l'única instància de la classe.

Patró Factory

El patró Factory proporciona una interfície per crear objectes en una superclasse, però permet que les subclasses alterin el tipus d'objectes que es crearan.

Implementació en Delphi

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

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

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

  TFactory = class
    function CreateProduct(AType: string): TProduct;
  end;

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

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

function TFactory.CreateProduct(AType: string): TProduct;
begin
  if AType = 'A' then
    Result := TConcreteProductA.Create
  else if AType = 'B' then
    Result := TConcreteProductB.Create
  else
    Result := nil;
end;

Explicació

  • Classe Producte: TProduct és una classe base amb un mètode abstracte Operation.
  • Classes Concretes: TConcreteProductA i TConcreteProductB són subclasses que implementen Operation.
  • Classe Fàbrica: TFactory conté el mètode CreateProduct que crea instàncies de TProduct basades en el tipus especificat.

Patrons Estructurals

Patró Adapter

El patró Adapter permet que classes amb interfícies incompatibles treballin juntes.

Implementació 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ó per defecte
end;

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

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

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

Explicació

  • Classe Target: TTarget defineix l'interfície que el client espera.
  • Classe Adaptee: TAdaptee conté una interfície incompatible que necessita ser adaptada.
  • Classe Adapter: TAdapter adapta l'interfície de TAdaptee a la interfície de TTarget.

Patrons de Comportament

Patró Observer

El patró Observer defineix una dependència un-a-molts entre objectes de manera que quan un objecte canvia d'estat, tots els seus dependents són notificats i actualitzats automàticament.

Implementació 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ó de l'actualització
end;

Explicació

  • Interfície Observer: IObserver defineix el mètode Update que serà cridat per TSubject.
  • Classe Subject: TSubject manté una llista d'observadors i els notifica quan hi ha un canvi d'estat.
  • Classe ConcreteObserver: TConcreteObserver implementa la interfície IObserver i defineix el comportament de l'actualització.

Exercicis Pràctics

Exercici 1: Implementar un Singleton

Implementa una classe Singleton que gestioni la configuració de l'aplicació.

Exercici 2: Crear una Fàbrica

Crea una fàbrica que generi diferents tipus de documents (per exemple, PDF, Word).

Exercici 3: Utilitzar un Adapter

Implementa un Adapter per adaptar una classe de registre de logs a una interfície de registre existent.

Exercici 4: Implementar un Observer

Crea un sistema de notificacions on diversos observadors reben actualitzacions d'un subjecte.

Solucions

Solució 1: Singleton

type
  TAppConfig = class
  private
    class var FInstance: TAppConfig;
    constructor Create; reintroduce;
  public
    class function GetInstance: TAppConfig;
  end;

constructor TAppConfig.Create;
begin
  inherited Create;
  // Inicialització de la configuració
end;

class function TAppConfig.GetInstance: TAppConfig;
begin
  if not Assigned(FInstance) then
    FInstance := TAppConfig.Create;
  Result := FInstance;
end;

Solució 2: Fàbrica

type
  TDocument = class
    procedure Open; virtual; abstract;
  end;

  TPDFDocument = class(TDocument)
    procedure Open; override;
  end;

  TWordDocument = class(TDocument)
    procedure Open; override;
  end;

  TDocumentFactory = class
    function CreateDocument(AType: string): TDocument;
  end;

procedure TPDFDocument.Open;
begin
  // Obrir document PDF
end;

procedure TWordDocument.Open;
begin
  // Obrir document Word
end;

function TDocumentFactory.CreateDocument(AType: string): TDocument;
begin
  if AType = 'PDF' then
    Result := TPDFDocument.Create
  else if AType = 'Word' then
    Result := TWordDocument.Create
  else
    Result := nil;
end;

Solució 3: Adapter

type
  TLogTarget = class
    procedure LogMessage(AMessage: string); virtual;
  end;

  TLegacyLogger = class
    procedure WriteLog(AMessage: string);
  end;

  TLogAdapter = class(TLogTarget)
  private
    FLegacyLogger: TLegacyLogger;
  public
    constructor Create(ALegacyLogger: TLegacyLogger);
    procedure LogMessage(AMessage: string); override;
  end;

procedure TLogTarget.LogMessage(AMessage: string);
begin
  // Implementació per defecte
end;

procedure TLegacyLogger.WriteLog(AMessage: string);
begin
  // Escriure log
end;

constructor TLogAdapter.Create(ALegacyLogger: TLegacyLogger);
begin
  FLegacyLogger := ALegacyLogger;
end;

procedure TLogAdapter.LogMessage(AMessage: string);
begin
  FLegacyLogger.WriteLog(AMessage);
end;

Solució 4: Observer

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ó de l'actualització
end;

Conclusió

En aquest tema, hem explorat diversos patrons de disseny i com implementar-los en Delphi/Object Pascal. Els patrons de disseny són eines poderoses que poden millorar significativament la qualitat del teu codi. Practica aquests patrons amb els exercicis proporcionats per consolidar el teu coneixement i prepara't per aplicar-los en projectes reals.

Curs de Programació Delphi/Object Pascal

Mòdul 1: Introducció a Delphi/Object Pascal

Mòdul 2: Estructures de Control i Procediments

Mòdul 3: Treballant amb Dades

Mòdul 4: Programació Orientada a Objectes

Mòdul 5: Funcions Avançades de Delphi

Mòdul 6: Desenvolupament d'Interfícies Gràfiques amb VCL i FMX

Mòdul 7: Desenvolupament Web i Mòbil

Mòdul 8: Millors Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2024. Tots els drets reservats