Template methode patroon - Template method pattern

Bij objectgeoriënteerd programmeren is de sjabloonmethode een van de gedragsontwerppatronen die zijn geïdentificeerd door Gamma et al. in het boek Design Patterns . De sjabloonmethode is een methode in een superklasse, meestal een abstracte superklasse, en definieert het skelet van een bewerking in termen van een aantal stappen op hoog niveau. Deze stappen worden zelf geïmplementeerd door aanvullende hulpmethoden in dezelfde klasse als de sjabloonmethode .

De hulpmethoden kunnen ofwel abstracte methoden zijn , in welk geval subklassen nodig zijn om concrete implementaties te verschaffen, of haakmethoden , die lege lichamen in de superklasse hebben. Subklassen kunnen (maar zijn niet verplicht) de bewerking aan te passen door de hook-methoden te overschrijven . De bedoeling van de sjabloonmethode is om de algehele structuur van de bewerking te definiëren, terwijl subklassen bepaalde stappen kunnen verfijnen of herdefiniëren.

Overzicht

Dit patroon bestaat uit twee hoofdonderdelen:

  • De "sjabloonmethode" wordt geïmplementeerd als een methode in een basisklasse (meestal een abstracte klasse ). Deze methode bevat code voor de delen van het algehele algoritme die invariant zijn. Het sjabloon zorgt ervoor dat het overkoepelende algoritme altijd wordt gevolgd. In de sjabloonmethode worden delen van het algoritme die kunnen variëren geïmplementeerd door zelfberichten te verzenden waarin wordt gevraagd om de uitvoering van aanvullende hulpmethoden . In de basisklasse krijgen deze hulpmethoden een standaardimplementatie of helemaal geen (dat wil zeggen dat het abstracte methoden kunnen zijn).
  • Subklassen van de basisklasse "vullen" de lege of "variant" delen van de "sjabloon" in met specifieke algoritmen die van de ene subklasse tot de andere verschillen. Het is belangrijk dat subklassen de sjabloonmethode zelf niet overschrijven .

Tijdens runtime wordt het algoritme dat wordt vertegenwoordigd door de sjabloonmethode uitgevoerd door het sjabloonbericht naar een instantie van een van de concrete subklassen te sturen. Door overerving begint de sjabloonmethode in de basisklasse te worden uitgevoerd. Wanneer de sjabloonmethode een bericht naar zichzelf stuurt met het verzoek om een ​​van de hulpmethoden, wordt het bericht ontvangen door de concrete subinstantie. Als de hulpmethode is overschreven, wordt de overheersende implementatie in de subinstantie uitgevoerd; als het niet is overschreven, wordt de overgeërfde implementatie in de basisklasse uitgevoerd. Dit mechanisme zorgt ervoor dat het algehele algoritme elke keer dezelfde stappen volgt, terwijl de details van sommige stappen afhangen van de instantie die het oorspronkelijke verzoek heeft ontvangen om het algoritme uit te voeren.

Dit patroon is een voorbeeld van inversie van controle omdat de code op hoog niveau niet langer bepaalt welke algoritmen moeten worden uitgevoerd; in plaats daarvan wordt tijdens runtime een algoritme op een lager niveau geselecteerd.

Sommige van de zelfberichten die door de sjabloonmethode worden verzonden, kunnen zijn om methoden te haken. Deze methoden zijn geïmplementeerd in dezelfde basisklasse als de sjabloonmethode, maar met lege lichamen (dwz ze doen niets). Er bestaan ​​hook-methoden zodat subklassen ze kunnen overschrijven, en dus de actie van het algoritme kunnen verfijnen zonder dat de sjabloonmethode zelf hoeft te worden overschreven. Met andere woorden, ze bieden een "haak" waaraan variantimplementaties kunnen worden "opgehangen".

Structuur

UML-klassendiagram

Image
Een voorbeeld van een UML-klassendiagram voor het ontwerppatroon van de sjabloonmethode.

In de bovenstaande UML klassediagram , het AbstractClass definieert een templateMethod() operatie die definieert het skelet (template) van een gedrag van

  • het implementeren van de onveranderlijke delen van het gedrag en
  • het naar zichzelf sturen van de berichten primitive1() en primitive2() , die, omdat ze zijn geïmplementeerd in SubClass1 , die subklasse toestaan ​​om een ​​variantimplementatie van die delen van het algoritme te bieden.
Image
Sjabloonmethode in LePUS3 .

Gebruik

De sjabloonmethode wordt gebruikt in frameworks, waarbij elk de onveranderlijke delen van de architectuur van een domein implementeert, terwijl het hook-methoden voor maatwerk biedt. Dit is een voorbeeld van omkering van controle . De sjabloonmethode wordt om de volgende redenen gebruikt.

  • Het laat subklassen variërend gedrag implementeren (door het overschrijven van de hook-methoden).
  • Het vermijdt duplicatie in de code: de algemene workflow van het algoritme wordt eenmaal geïmplementeerd in de sjabloonmethode van de abstracte klasse en de nodige variaties worden geïmplementeerd in de subklassen.
  • Het controleert de punt (en) waarop specialisatie is toegestaan. Als de subklassen simpelweg de sjabloonmethode zouden overschrijven, zouden ze radicale en willekeurige wijzigingen in de workflow kunnen aanbrengen. Door daarentegen alleen de hook-methoden te overschrijven, kunnen alleen bepaalde specifieke details van de workflow worden gewijzigd en blijft de algehele workflow intact.

Gebruik met codegeneratoren

Het sjabloonpatroon is handig bij het werken met automatisch gegenereerde code. De uitdaging van het werken met gegenereerde code is dat wijzigingen in de broncode leiden tot wijzigingen in de gegenereerde code; als er handgeschreven wijzigingen zijn aangebracht in de gegenereerde code, gaan deze verloren. Hoe moet de gegenereerde code dan worden aangepast?

Het sjabloonpatroon biedt een oplossing. Als de gegenereerde code het patroon van de sjabloonmethode volgt, zal de gegenereerde code allemaal een abstracte superklasse zijn. Op voorwaarde dat handgeschreven aanpassingen beperkt blijven tot een subklasse, kan de codegenerator opnieuw worden uitgevoerd zonder het risico te lopen deze wijzigingen te overschrijven. Bij gebruik met codegeneratie wordt dit patroon soms het generatiekloofpatroon genoemd .

PHP-voorbeeld

abstract class Game
{
    abstract protected function initialize();
    abstract protected function startPlay();
    abstract protected function endPlay();

    /** Template method */
    public final function play()
    {
        /** Primitive */
        $this->initialize();

        /** Primitive */
        $this->startPlay();

        /** Primitive */
        $this->endPlay();
    }
}

class Mario extends Game
{
    protected function initialize()
    {
        echo "Mario Game Initialized! Start playing.", PHP_EOL;
    }

    protected function startPlay()
    {
        echo "Mario Game Started. Enjoy the game!", PHP_EOL;
    }

    protected function endPlay()
    {
        echo "Mario Game Finished!", PHP_EOL;
    }

}

class Tankfight extends Game
{
    protected function initialize()
    {
        echo "Tankfight Game Initialized! Start playing.", PHP_EOL;
    }

    protected function startPlay()
    {
        echo "Tankfight Game Started. Enjoy the game!", PHP_EOL;
    }

    protected function endPlay()
    {
        echo "Tankfight Game Finished!", PHP_EOL;
    }

}

$game = new Tankfight();
$game->play();

$game = new Mario();
$game->play();

Zie ook

Referenties

Externe links