„Schablonenmethode“ – Versionsunterschied

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen
[gesichtete Version][gesichtete Version]
Inhalt gelöscht Inhalt hinzugefügt
FelixOT (Diskussion | Beiträge)
PNG mit SVG ersetzt
→‎Beispiel: C++14 Implementierung des Beispielcode aus Buch Entwurfsmuster S. 366 ff.
 
Zeile 17: Zeile 17:


== Beispiel ==
== Beispiel ==
Diese C++14 Implementierung basiert auf der vor C++98 Implementierung im Buch Entwurfsmuster.

Der grobe Ablauf eines Brettspiels wie [[Schach]] oder [[Monopoly]] sieht immer gleich aus: Es wird eine Begrüßung ausgesprochen und das Brett wird aufgestellt. Danach sind die Spieler reihum so lange am Zug, bis das Spiel beendet ist. Zum Schluss wird der Gewinner festgestellt.

Das Skelett dieses immer gleichen Algorithmus lässt sich in seinen Grundzügen in einer Schablonenmethode einer abstrakten Klasse <code>Game</code> implementieren. Um nun ein konkretes Spiel zu implementieren, müssen die abstrakten Methoden der Schablonenmethode (die sich für jedes Brettspiel unterscheiden) in einer konkreten Kindklasse implementiert werden. Hier ein Beispiel in [[C++]].

<syntaxhighlight lang="cpp">
<syntaxhighlight lang="cpp">
#include <iostream>
#include <iostream>
#include <cstring>
#include <memory>

using std::cout;
using std::endl;

class Game
{
protected:
int m_iPlayersCount;

Game(const int iPLAYERS_COUNT)
{
m_iPlayersCount = iPLAYERS_COUNT;
}

virtual void printGreeting()
{
cout << "Welcome to our wonderful game!" << endl;
}

// Einschub-Methode:
virtual void initializeBoard() = 0;
virtual void makeMove(const int iPLAYER) = 0;
virtual bool gameFinished() = 0;
virtual void printWinner() = 0;

// Hook-Methode:
virtual void takeADrink(const int iPLAYER) { }


class View { // AbstraktKlasse
public:
public:
// definiert abstrakte primitive Operationen, die von konkreten Unterklassen definiert werden, um die Schritte eines Algorithmus zu implementieren.
// Schablonen-Methode
void playOneGame()
virtual void doDisplay() {}
// implementiert eine Schablonenmethode zur Definition des Skeletts eines Algorithmus. Die Schablonenmethode ruft sowohl primitive Operationen als auch in AbstraktKlasse oder anderen Klassen definierte Operationen auf.
{
void display() {
printGreeting();
initializeBoard();
setFocus();
int i = 0;
doDisplay();
resetFocus();

while (!gameFinished())
{
takeADrink(i); // Aufruf des Hook (standardmäßig leer)
makeMove(i);
i = (i + 1) % m_iPlayersCount;
}

printWinner();
}
}
virtual ~View() = default;
};
private:

void setFocus() {
class Chess : public Game
std::cout << "View::setFocus\n";
{
protected:
void initializeBoard()
{
// ...
}
}
void makeMove(const int iPLAYER)
void resetFocus() {
std::cout << "View::resetFocus\n";
{
cout << "Player " << iPLAYER << "'s turn in chess game" << endl;
// ...
}
}
bool gameFinished()
{
// ...
}
void printWinner()
{
// ...
}

public:
Chess() : Game (2) { }
};
};


class Monopoly : public Game
class MyView : public View { // KonkreteKlasse
// implementiert die primitiven Operationen, welche die unterklassenspezifischen Schritte des Algorithmus ausführen.
{
void doDisplay() override {
protected:
// stelle den Inhalt der Anzeige dar
void printGreeting()
std::cout << "MyView::doDisplay\n";
{
cout << "Welcome to monopoly!" << endl;
}
}
void initializeBoard()
{
// ...
}
void makeMove(const int iPLAYER)
{
cout << "Player " << iPLAYER << "'s turn in monopoly game" << endl;
// ...
}
bool gameFinished()
{
// ...
}
void printWinner()
{
// ...
}

public:
Monopoly(const int iPLAYERS_COUNT) : Game(iPLAYERS_COUNT) { }
};
};


int main() {
class DrinkersMonopoly : public Monopoly
// Die Smart pointers verhindern Memory leaks.
{
std::unique_ptr<View> myview = std::make_unique<MyView>();
protected:
myview->display();
void printGreeting()
}
{
</syntaxhighlight>
Monopoly::printGreeting();
cout << "(The drinkers' version)" << endl;
}
void takeADrink(const int iPLAYER)
{
cout << "Player " << iPLAYER << " drinks a glass of whiskey" << endl;
}


Die Programmausgabe ist:
public:
DrinkersMonopoly(const int iPLAYERS_COUNT) : Monopoly(iPLAYERS_COUNT) { }
};


<syntaxhighlight lang="cpp">
int main(int iArgc, char* pa_Argv[])
View::setFocus
{
MyView::doDisplay
Game* p_MyGame;
View::resetFocus

if (iArgc <= 1 || strcmp(pa_Argv[1], "Chess") == 0)
{
p_MyGame = new Chess();
}
else if (strcmp(pa_Argv[1], "Monopoly") == 0)
{
p_MyGame = new Monopoly(4);
}
else if (strcmp(pa_Argv[1], "DrinkersMonopoly") == 0)
{
p_MyGame = new DrinkersMonopoly(4);
}
else
{
cout << "Unknown game." << endl;
return 1;
}

p_MyGame->playOneGame();
delete p_MyGame;
return 0;
}
</syntaxhighlight>
</syntaxhighlight>



Aktuelle Version vom 26. April 2023, 16:58 Uhr

Klassendiagramm einer Schablonenmethoden-Implementierung

Die Schablonenmethode (englisch template method pattern) ist ein in der Softwareentwicklung eingesetztes Entwurfsmuster, mit dem Teilschritte eines Algorithmus variabel gehalten werden können.[1] Es gehört zur Kategorie der Verhaltensmuster (engl. behavioral patterns). Das Muster ist eines der sogenannten Viererbanden-Entwurfsmuster (GoF).

Beim Schablonenmethoden-Entwurfsmuster wird in einer abstrakten Klasse das Skelett eines Algorithmus definiert. Die konkrete Ausformung der einzelnen Schritte wird an Unterklassen delegiert. Dadurch besteht die Möglichkeit, einzelne Schritte des Algorithmus zu verändern oder zu überschreiben, ohne dass die zu Grunde liegende Struktur des Algorithmus modifiziert werden muss. Die Schablonenmethode (engl. template method) ruft abstrakte Methoden auf, die erst in den Unterklassen definiert werden. Diese Methoden werden auch als Einschubmethoden bezeichnet.

Zusätzlich können in der Schablonenmethode an bestimmten Stellen Hook-Operationen aufgerufen werden, deren Standardimplementierung in der abstrakten Klasse nichts tut. Auf diese Weise kann man an vordefinierten Stellen im Algorithmus zusätzliche Funktionalität einfügen.

Als Variante können die Einschubmethoden oder Hook-Operationen auch eine Standard-Implementierung besitzen, die von den konkreten Klassen genutzt werden können, aber nicht müssen.

Ein Beispiel dazu findet sich in der I/O-Datenstrom-Programmierschnittstelle von Java. Dort implementiert ein OutputStream eine konkrete Methode zum Schreiben eines Byte-Arrays. Diese Methode benutzt eine Methode zum Schreiben eines einzelnen Bytes, um das ganze Array nach und nach zu schreiben. Die Methode für das einzelne Byte ist jedoch noch abstrakt, da ein OutputStream selbst noch nicht spezifisch ist. Klassen wie FileOutputStream können diese Methode implementieren. Sie erben dann eine bereits implementierte Methode zum Schreiben eines Byte-Arrays.

Policy-Based Design

[Bearbeiten | Quelltext bearbeiten]

Policy-Based Design ist ein allgemeineres Entwurfsmuster, bei dem nicht nur Algorithmen, sondern ganze Klassen schablonenhaft aufgebaut sind. Sowohl Methoden bzw. Algorithmen als auch gespeicherte Datensätze, Basisklassen und Schnittstellen sind dann innerhalb der Skelettstruktur austauschbar. Dies erfordert in aller Regel Templatemetaprogrammierung, wie es sie in C++ und D gibt, ist jedoch theoretisch auch über Konstrukte einiger Skriptsprachen (eval, Makros, Autoloading von Quellcode o. Ä.), allerdings kaum in Sprachen wie C# oder Java realisierbar.

Diese C++14 Implementierung basiert auf der vor C++98 Implementierung im Buch Entwurfsmuster.

#include <iostream>
#include <memory>

class View { // AbstraktKlasse
public:
  // definiert abstrakte primitive Operationen, die von konkreten Unterklassen definiert werden, um die Schritte eines Algorithmus zu implementieren.
  virtual void doDisplay() {}
  // implementiert eine Schablonenmethode zur Definition des Skeletts eines Algorithmus. Die Schablonenmethode ruft sowohl primitive Operationen als auch in AbstraktKlasse oder anderen Klassen definierte Operationen auf.
  void display() {
    setFocus();
    doDisplay();
    resetFocus();
  }
  virtual ~View() = default;
private:
  void setFocus() {
    std::cout << "View::setFocus\n";
  }
  void resetFocus() {
    std::cout << "View::resetFocus\n";
  }
};

class MyView : public View { // KonkreteKlasse
  // implementiert die primitiven Operationen, welche die unterklassenspezifischen Schritte des Algorithmus ausführen.
  void doDisplay() override {
    // stelle den Inhalt der Anzeige dar
    std::cout << "MyView::doDisplay\n";
  }
};

int main() {
  // Die Smart pointers verhindern Memory leaks.
  std::unique_ptr<View> myview = std::make_unique<MyView>();
  myview->display();
}

Die Programmausgabe ist:

View::setFocus
MyView::doDisplay
View::resetFocus

Einzelnachweise

[Bearbeiten | Quelltext bearbeiten]
  1. Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, 1996, ISBN 3-8273-1862-9, S. 366.