Das Strategy-Muster

Beim Strategy-Muster geht es darum verschiedene Verhaltensweisen (Algorithmen) zu kapseln, um sie dann den Client über eine Schnittstelle zur Verfügung zu stellen. Dadurch ist es möglich dem Client ein neues Verhalten leicht hinzuzufügen oder zur Laufzeit zu ändern.

Im Buch „Entwurfsmuster von Kopf bis Fuß“ ist das sehr schön mit verschiedenen Enten und ihren unterschiedlichen Flug- und Quakverhalten erklärt. Ich versuche es hier, wiederum inspiriert aus dem Buch, mit einem Computerspiel. Ich möchte zwei Varianten beschreiben. Ein mal mit Hilfe eines Interfaces und ein mal mit einer Klasse.

Gegeben seine Spielfiguren (Ritter, Bauer Troll) mit ihren Waffen (Schwert und Messer). Die Spielfiguren verhalten sich abhängig von Ihren Waffen im Kampf unterschiedlich.

Variante1:


// --------------
//
// Delphi Entwursmustersammlung  -- Strategy (mit Interface)
//
// Autor: Thomas Groeschke
// Erstellt: 24.08.2008
//
// --------------
unit Strategy01;

interface

uses Dialogs;

type

// ---------------
// Waffenverhalten
// ---------------

  IWaffenVerhalten = interface(IInterface)
    procedure VerwendeWaffe;
  end;

  TAxtVerhalten = class(TInterfacedObject, IWaffenVerhalten)
    procedure VerwendeWaffe;
  end;

  TMesserVerhalten = class(TInterfacedObject, IWaffenVerhalten)
    procedure VerwendeWaffe;
  end;

// ---------------
// Figuren
// ---------------

  TFigur = class(TObject)
  private
    FWaffenVerhalten: IWaffenVerhalten;
    Name: string;
  public
    constructor Create(iName: string; iActWaffenVerhalten: IWaffenVerhalten);
    destructor Destroy; override;
    procedure Kaempfen; virtual; abstract;
    procedure SetWaffe(iActWaffenVerhalten: IWaffenVerhalten);
  end;

  TRitter = class(TFigur)
    procedure Kaempfen; override;
  end;

  TBauer = class(TFigur)
  public
    procedure Kaempfen; override;
  end;

  TTroll = class(TFigur)
    procedure Kaempfen; override;
  end;

implementation

{$REGION 'Waffenverhalten'}

procedure TAxtVerhalten.VerwendeWaffe;
begin
  ShowMessage('Axt wird geschwungen');
end;

procedure TMesserVerhalten.VerwendeWaffe;
begin
  ShowMessage('Mit Messer wird gestochen');
end;

{$ENDREGION}

{$REGION 'Basisklasse Figur'}

constructor TFigur.Create(iName: string; iActWaffenVerhalten: IWaffenVerhalten);
begin
  inherited create;
  Name := iName;
  FWaffenVerhalten := iActWaffenVerhalten;
end;

destructor TFigur.Destroy;
begin
  // Das Verhalten darf nicht freigegeben werden, da es ja
  // von außen "reingereicht" worden ist.
  inherited;
end;

procedure TFigur.SetWaffe(iActWaffenVerhalten: IWaffenVerhalten);
begin
  FWaffenVerhalten := iActWaffenVerhalten;
end;

{$ENDREGION}

{$REGION 'Figuren Ritter, Troll und Bauer'}

procedure TRitter.Kaempfen;
begin
  if FWaffenVerhalten <> nil then
    FWaffenVerhalten.VerwendeWaffe;
  // weitere Aktionen...
end;

procedure TTroll.Kaempfen;
begin
  if FWaffenVerhalten <> nil then
    FWaffenVerhalten.VerwendeWaffe;
  // weitere Aktionen...
end;

procedure TBauer.Kaempfen;
begin
  if FWaffenVerhalten <> nil then
     FWaffenVerhalten.VerwendeWaffe
  // weitere Aktionen...
end;

{$ENDREGION}

Etwas Quellcode zum Testen:

procedure TForm1.Button1Click(Sender: TObject);
var
  Messer : IWaffenVerhalten;
  Axt : IWaffenVerhalten;
  Ritter1 : TFigur;
  Ritter2 : TFigur;
  Bauer : TFigur;
  Troll : TFigur;
begin
  // -----------
  // Nur hier, beim Erzeugen der konkreten Objekte, wird der tatsächliche
  // Typ (TMesser, TAxt, TRitter u.s.w. genannt.
  // -----------
  // Verhalten
  Messer := TMesserVerhalten.Create;
  Axt := TAxtVerhalten.Create;
  // Der Client
  Ritter1 := TRitter.Create('Gerhard, der Stolze',Messer);
  Ritter2 := TRitter.Create('Otto, der Dumme',nil);
  Bauer := TBauer.Create('Josef',nil);
  Troll := TTroll.Create('Rumpelstielzchen',Axt);

  Troll.Kaempfen;
  Ritter1.Kaempfen;
  Ritter2.Kaempfen;
  Bauer.Kaempfen;
  Troll.Kaempfen;

  // Ritter2 bekommt Messer
  Ritter2.SetWaffe(Messer);
  Ritter2.Kaempfen;

  Troll.Free;
  Bauer.Free;
  Ritter2.Free;
  Ritter1.Free;

end;

Variante2:

// --------------
//
// Delphi Entwursmustersammlung  -- Strategy (mit Klasse)
//
// Autor: Thomas Groeschke
// Erstellt: 24.08.2008
//
// --------------
unit Strategy02;

interface

uses Dialogs;

type

// ---------------
// Waffenverhalten
// ---------------

  TWaffenVerhalten02 = class(TObject)
  private
    name: string;
  public
    constructor create(iName:string);
    procedure VerwendeWaffe; virtual;
  end;

  TAxtVerhalten02 = class(TWaffenVerhalten02)
    procedure VerwendeWaffe; override;
  end;

  TMesserVerhalten02 = class(TWaffenVerhalten02)
    procedure VerwendeWaffe; override;
  end;

// ---------------
// Figuren
// ---------------

  TFigur02 = class(TObject)
  private
    FWaffenVerhalten02: TWaffenVerhalten02;
    Name: string;
  public
    constructor Create(iName: string; iActWaffenVerhalten02: TWaffenVerhalten02);
    destructor Destroy; override;
    procedure Kaempfen; virtual;
    procedure SetWaffe(iActWaffenVerhalten02: TWaffenVerhalten02);
  end;

  TRitter02 = class(TFigur02)
    procedure Kaempfen; override;
  end;

  TBauer02 = class(TFigur02)
  public
    procedure Kaempfen; override;
  end;

  TTroll02 = class(TFigur02)
    procedure Kaempfen; override;
  end;

implementation

{$REGION 'Waffenverhalten'}

constructor TWaffenVerhalten02.create(iName:string);
begin
  Name := iName;
end;

procedure TWaffenVerhalten02.VerwendeWaffe;
begin
  ShowMessage('Weglaufen');
end;

procedure TAxtVerhalten02.VerwendeWaffe;
begin
  ShowMessage('Axt wird geschwungen');
end;

procedure TMesserVerhalten02.VerwendeWaffe;
begin
  ShowMessage('Mit Messer wird gestochen');
end;

{$ENDREGION}

{$REGION 'Basisklasse Figur'}

constructor TFigur02.Create(iName: string; iActWaffenVerhalten02:
    TWaffenVerhalten02);
begin
  inherited create;
  Name := iName;
  FWaffenVerhalten02 := iActWaffenVerhalten02;
end;

destructor TFigur02.Destroy;
begin
  // Das Verhalten darf nicht freigegeben werden, da es ja
  // von außen "reingereicht" worden ist.
  inherited;
end;

procedure TFigur02.Kaempfen;
begin
  if FWaffenVerhalten02 <> nil then
    FWaffenVerhalten02.VerwendeWaffe;
end;

procedure TFigur02.SetWaffe(iActWaffenVerhalten02: TWaffenVerhalten02);
begin
  FWaffenVerhalten02 := iActWaffenVerhalten02;
end;

{$ENDREGION}

{$REGION 'Figuren Ritter, Troll und Bauer'}

procedure TRitter02.Kaempfen;
begin
  inherited;
  // weitere Aktionen
end;

procedure TTroll02.Kaempfen;
begin
  inherited;
  // weitere Aktionen
end;

procedure TBauer02.Kaempfen;
begin
  inherited;
  // weitere Aktionen
end;

{$ENDREGION}

end.

Und auch hier etwas zum Testen.

procedure TForm1.Button2Click(Sender: TObject);
var
  Messer : TWaffenVerhalten02;
  Axt : TWaffenVerhalten02;
  BasisWaffe : TWaffenVerhalten02;
  Ritter1 : TFigur02;
  Ritter2 : TFigur02;
  Bauer : TFigur02;
  Troll : TFigur02;
begin
  // -----------
  // Nur hier, beim Erzeugen der konkreten Objekte, wird der tatsächliche
  // Typ (TMesser, TAxt, TRitter u.s.w. genannt.
  // -----------
  // Verhalten
  Messer := TMesserVerhalten02.Create('Messer');
  Axt := TAxtVerhalten02.Create('Axt');
  BasisWaffe := TWaffenVerhalten02.create('Standardwaffe');
  // Der Client
  Ritter1 := TRitter02.Create('Gerhard, der Stolze',Messer);
  Ritter2 := TRitter02.Create('Otto, der Dumme',BasisWaffe);
  Bauer := TBauer02.Create('Josef',BasisWaffe);
  Troll := TTroll02.Create('Rumpelstielzchen',Axt);

  Troll.Kaempfen;
  Ritter1.Kaempfen;
  Ritter2.Kaempfen;
  Bauer.Kaempfen;
  Troll.Kaempfen;

  // Ritter2 bekommt Messer
  Ritter2.SetWaffe(Messer);
  Ritter2.Kaempfen;

  Troll.Free;
  Bauer.Free;
  Ritter2.Free;
  Ritter1.Free;
  BasisWaffe.Free;
  Axt.Free;
  Messer.Free;
end;

Links zum Thema:

Wikipedia

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>