Lezers zoals jij steunen MUO. Wanneer u een aankoop doet via links op onze site, kunnen we een aangesloten commissie verdienen. Lees verder.

Een van de belangrijkste principes bij softwareontwikkeling is het open-gesloten ontwerpprincipe. Dit ontwerpprincipe benadrukt dat klassen open moeten staan ​​voor uitbreiding, maar gesloten voor wijziging. Het ontwerppatroon van de decorateur belichaamt het open-gesloten ontwerpprincipe.

Met het decorateur-ontwerppatroon kunt u een klasse eenvoudig uitbreiden door deze een nieuw gedrag te geven zonder de bestaande code te wijzigen. Het decorateurpatroon doet dit dynamisch tijdens runtime, met behulp van compositie. Dit ontwerppatroon staat bekend als een flexibel alternatief voor het gebruik van overerving om gedrag uit te breiden.

Hoe werkt het Decorator-ontwerppatroon?

Hoewel het decorateurpatroon een alternatief is voor klasse erfenis, neemt het enkele aspecten van overerving op in zijn ontwerp. Een belangrijk aspect van het decorateurpatroon is dat al zijn klassen direct of indirect gerelateerd zijn.

Een typisch decorontwerppatroon heeft de volgende structuur:

Uit het bovenstaande klassendiagram kunt u zien dat het decorateurpatroon vier hoofdklassen heeft.

Onderdeel: dit is een abstracte klasse (of interface), die dient als het supertype voor het decorateurpatroon.

BetonOnderdeel: dit zijn de objecten die je tijdens runtime kunt decoreren met verschillende gedragingen. Ze erven van de componentinterface en implementeren de abstracte functies ervan.

Decorateur: deze klasse is abstract en heeft hetzelfde supertype als het object dat hij zal versieren. In het klassendiagram ziet u twee relaties tussen de klassen component en decorateur. De eerste relatie is er een van overerving; elke binnenhuisarchitect is een bestanddeel. De tweede relatie is er een van compositie; elke binnenhuisarchitect heeft een (of wikkelt een) component.

Betondecorateur: dit zijn de individuele decorateurs die een onderdeel een specifiek gedrag geven. Houd er rekening mee dat elke betondecorateur een instantievariabele heeft die een verwijzing naar een component bevat.

Implementatie van het Decorator Design Pattern in Java

Een voorbeeldtoepassing voor het bestellen van pizza's kan voldoende demonstreren hoe het decoratiepatroon moet worden gebruikt om toepassingen te ontwikkelen. Met deze voorbeeldpizza-applicatie kunnen klanten pizza's bestellen met meerdere toppings. De eerste klasse van het decorateurpatroon is de pizza-interface:

openbaarkoppelPizza{
openbaarabstract Snaar beschrijving();
openbaarabstractdubbelekosten();
}

De Pizza-interface is de componentklasse. U kunt er dus een of meer concrete klassen van maken. Het pizzabedrijf maakt twee hoofdsoorten pizza's op basis van hun deeg. Eén type pizza heeft gistdeeg:

openbaarklasGistCrustPizzaimplementeertPizza{
@Overschrijven
openbaar Snaar beschrijving(){
opbrengst"Pizzadeeg gemaakt met gist";
}

@Overschrijven
openbaardubbelekosten(){
opbrengst18.00;
}
}

De YeastCrustPizza is het eerste beton Java-klasse van de Pizza-interface. Het andere beschikbare type pizza is flatbread:

openbaarklasFlatbreadCrustPizzaimplementeertPizza{
@Overschrijven
openbaar Snaar beschrijving(){
opbrengst"Pizzadeeg gemaakt met flatbread";
}

@Overschrijven
openbaardubbelekosten(){
opbrengst15.00;
}
}

De klasse FlatbreadCrustPizza is de tweede concrete component en implementeert, net als de klasse YeastCrustPizza, alle abstracte functies van de Pizza-interface.

De decorateurs

De decorateurklasse is altijd abstract, dus u kunt er niet rechtstreeks een nieuwe instantie van maken. Maar het is noodzakelijk om een ​​relatie tot stand te brengen tussen de verschillende decorateurs en de componenten die ze zullen decoreren.

openbaarabstractklasToppingDecorateurimplementeertPizza{
openbaar Snaar beschrijving(){
opbrengst"Onbekend topping";
}
}

De klasse ToppingDecorator vertegenwoordigt de decorateurklasse in deze voorbeeldtoepassing. Nu kan het pizzabedrijf veel verschillende toppings (of decorateurs) maken met behulp van de klasse ToppingDecorator. Laten we zeggen dat een pizza drie verschillende soorten toppings kan hebben, namelijk kaas, pepperoni en champignons.

Kaas Topping

openbaarklasKaasstrekt zich uitToppingDecorateur{
privaat Pizza pizza;

openbaarKaas(Pizza pizza){
dit.pizza = pizza;
}

@Overschrijven
openbaar Snaar beschrijving(){
opbrengst pizza.beschrijving() + ", Kaas Topping";
}

@Overschrijven
openbaardubbelekosten(){
opbrengstpizza.kosten() + 2.50;
}
}

Pepperoni Topping

openbaarklasPepperonistrekt zich uitToppingDecorateur{
privaat Pizza pizza;

openbaarPepperoni(Pizza pizza){
dit.pizza = pizza;
}

@Overschrijven
openbaar Snaar beschrijving(){
opbrengst pizza.beschrijving() + ", Pepperoni Topping";
}

@Overschrijven
openbaardubbelekosten(){
opbrengstpizza.kosten() + 3.50;
}
}

Champignon Topping

openbaarklasPaddestoelstrekt zich uitToppingDecorateur{
privaat Pizza pizza;

openbaarPaddestoel(Pizza pizza){
dit.pizza = pizza;
}

@Overschrijven
openbaar Snaar beschrijving(){
opbrengst pizza.beschrijving() + ", Champignon Topping";
}

@Overschrijven
openbaardubbelekosten(){
opbrengstpizza.kosten() + 4.50;
}
}

Nu heb je een eenvoudige applicatie geïmplementeerd met behulp van het ontwerppatroon van de decorateur. Als een klant een pizza met gistkorst met kaas en pepperoni zou bestellen, ziet de testcode voor dat scenario er als volgt uit:

openbaarklasVoornaamst{
openbaarstatischleegtevoornaamst(String[] argumenten){
Pizzapizza1 = nieuw GistKorstPizza();
pizza1 = nieuw Pepperoni (pizza1);
pizza1 = nieuw Kaas (pizza1);
Systeem.out.println (pizza1.description() + " $" + pizza1.kosten());
}
}

Als u deze code uitvoert, wordt de volgende uitvoer in de console geproduceerd:

Zoals u kunt zien, vermeldt de uitvoer het type pizza samen met de totale kosten. De pizza begon als een pizza met gistkorst voor $ 18,00, maar met het decoratiepatroon kon de applicatie nieuwe functies en de bijbehorende kosten aan de pizza toevoegen. Dus de pizza een nieuw gedrag geven zonder de bestaande code (de pizza met gistkorst) te wijzigen.

Met het decorateurpatroon kunt u hetzelfde gedrag zo vaak als u wilt op een object toepassen. Als een klant een pizza bestelt met alles erop en eraan en wat extra kaas, kun je de hoofdklasse updaten met de volgende code om dit weer te geven:

Pizzapizza2 = nieuw GistKorstPizza();
pizza2 = nieuw Pepperoni (pizza2);
pizza2 = nieuw Kaas (pizza2);
pizza2 = nieuw Kaas (pizza2);
pizza2 = nieuw Champignon (pizza2);

Systeem.out.println (pizza2.description() + " $" + pizza2.kosten());

De bijgewerkte toepassing produceert de volgende uitvoer in de console:

De voordelen van het gebruik van het Decorator-ontwerppatroon

De twee belangrijkste voordelen van het gebruik van het decorateurontwerppatroon zijn veiligheid en flexibiliteit. Met het decorateurpatroon kunt u veiligere code ontwikkelen door de reeds bestaande beveiligde code niet te verstoren. Het breidt in plaats daarvan bestaande code uit door middel van compositie. Het effectief voorkomen van de introductie van nieuwe bugs of onbedoelde bijwerkingen.

Door de samenstelling heeft een ontwikkelaar ook veel flexibiliteit bij het gebruik van het decorateurpatroon. U kunt op elk moment een nieuwe decorateur implementeren om nieuw gedrag toe te voegen, zonder de bestaande code te wijzigen en de toepassing te verstoren.