Läsare som du hjälper till att stödja MUO. När du gör ett köp med hjälp av länkar på vår webbplats kan vi tjäna en affiliate-provision. Läs mer.

En av de viktigaste principerna inom mjukvaruutveckling är den öppna-stängda designprincipen. Denna designprincip betonar att klasser ska vara öppna för förlängning, men stängda för modifiering. Dekoratörens designmönster förkroppsligar designprincipen med öppet och stängt.

Med dekoratörens designmönster kan du enkelt utöka en klass genom att ge den ett nytt beteende utan att ändra dess befintliga kod. Dekoratörsmönstret gör detta dynamiskt under körning, med hjälp av komposition. Detta designmönster är känt som ett flexibelt alternativ till att använda arv för att utöka beteendet.

Hur fungerar dekorationsmönstret?

Fast dekorationsmönstret är ett alternativ till klass arv, den innehåller vissa aspekter av arv i sin design. En nyckelaspekt av dekoratörsmönstret är att alla dess klasser är relaterade, antingen direkt eller indirekt.

Ett typiskt designmönster för dekoratörer har följande struktur:

instagram viewer

Från klassdiagrammet ovan kan du se att dekoratörsmönstret har fyra huvudklasser.

Komponent: detta är en abstrakt klass (eller gränssnitt), som fungerar som supertyp för dekorationsmönstret.

Betongkomponent: det här är föremålen som du kan dekorera med olika beteenden under körning. De ärver från komponentgränssnittet och implementerar dess abstrakta funktioner.

Dekoratör: den här klassen är abstrakt och har samma supertyp som föremålet den ska dekorera. I klassdiagrammet ser du två relationer mellan komponent- och dekorationsklasserna. Det första förhållandet är ett arv; varje dekoratör är en komponent. Det andra förhållandet är ett av komposition; varje dekoratör har en (eller omsluter en) komponent.

Betongdekoratör: det är de individuella dekoratörerna som ger en komponent ett specifikt beteende. Du bör notera att varje betongdekoratör har en instansvariabel som innehåller en referens till en komponent.

Implementering av Decorator Design Pattern i Java

Ett exempel på en pizzabeställningsapplikation kan visa hur man använder dekorationsmönstret för att utveckla applikationer. Denna provpizzaapplikation låter kunder beställa pizzor med flera pålägg. Den första klassen i dekorationsmönstret är pizzagränssnittet:

offentliggränssnittPizza{
offentligabstrakt Sträng beskrivning();
offentligabstraktdubbelkosta();
}

Pizza-gränssnittet är komponentklassen. Så du kan skapa en eller flera konkreta klasser från den. Pizzaföretaget gör två huvudtyper av pizzor, baserat på deras deg. En typ av pizza har jästdeg:

offentligklassYeastCrustPizzaredskapPizza{
@Åsidosätta
offentlig Sträng beskrivning(){
lämna tillbaka"Pizzadeg gjord med jäst";
}

@Åsidosätta
offentligdubbelkosta(){
lämna tillbaka18.00;
}
}

YeastCrustPizzan är den första betongen Java klass av Pizza-gränssnittet. Den andra typen av pizza som finns är tunnbröd:

offentligklassFlatbreadCrustPizzaredskapPizza{
@Åsidosätta
offentlig Sträng beskrivning(){
lämna tillbaka"Pizzadeg gjord på tunnbröd";
}

@Åsidosätta
offentligdubbelkosta(){
lämna tillbaka15.00;
}
}

Klassen FlatbreadCrustPizza är den andra konkreta komponenten och, liksom YeastCrustPizza-klassen, implementerar den alla abstrakta funktioner i Pizza-gränssnittet.

Dekoratörerna

Dekoratörsklassen är alltid abstrakt, så du kan inte skapa en ny instans direkt från den. Men det är nödvändigt att etablera en relation mellan de olika dekoratörerna och de komponenter som de ska dekorera.

offentligabstraktklassToppingDecoratorredskapPizza{
offentlig Sträng beskrivning(){
lämna tillbaka"Okänd topping";
}
}

Klassen ToppingDecorator representerar dekoratörsklassen i denna exempelapplikation. Nu kan pizzaföretaget skapa många olika pålägg (eller dekoratörer), med hjälp av ToppingDecorator-klassen. Låt oss säga att en pizza kan ha tre olika typer av pålägg, nämligen ost, pepperoni och svamp.

Ost Topping

offentligklassOststräcker sigToppingDecorator{
privat Pizza pizza;

offentligOst(Pizza pizza){
detta.pizza = pizza;
}

@Åsidosätta
offentlig Sträng beskrivning(){
lämna tillbaka pizza.description() + ", Ost Topping";
}

@Åsidosätta
offentligdubbelkosta(){
lämna tillbakapizza.kosta() + 2.50;
}
}

Pepperoni Topping

offentligklassPepperonisträcker sigToppingDecorator{
privat Pizza pizza;

offentligPepperoni(Pizza pizza){
detta.pizza = pizza;
}

@Åsidosätta
offentlig Sträng beskrivning(){
lämna tillbaka pizza.description() + ", Pepperoni Topping";
}

@Åsidosätta
offentligdubbelkosta(){
lämna tillbakapizza.kosta() + 3.50;
}
}

Svamptopping

offentligklassSvampsträcker sigToppingDecorator{
privat Pizza pizza;

offentligSvamp(Pizza pizza){
detta.pizza = pizza;
}

@Åsidosätta
offentlig Sträng beskrivning(){
lämna tillbaka pizza.description() + ", Svamptopping";
}

@Åsidosätta
offentligdubbelkosta(){
lämna tillbakapizza.kosta() + 4.50;
}
}

Nu har du en enkel applikation implementerad med hjälp av dekoratörens designmönster. Om en kund skulle beställa en jästcrustpizza med ost och pepperoni, kommer testkoden för det scenariot att se ut så här:

offentligklassMain{
offentligstatisktomhethuvud(String[] args){
Pizza pizza1 = ny YeastCrustPizza();
pizza1 = ny Pepperoni (pizza1);
pizza1 = ny Ost (pizza1);
System.out.println (pizza1.description() + " $" + pizza1.cost());
}
}

Att köra den här koden kommer att producera följande utdata i konsolen:

Som du kan se anger utdata typen av pizza tillsammans med dess totala kostnad. Pizzan började som en jästcrustpizza för $18,00, men med dekorationsmönstret kunde applikationen lägga till nya funktioner och deras lämpliga kostnad för pizzan. Alltså, ge pizzan nytt beteende utan att ändra den befintliga koden (jästcrustpizzan).

Med dekoratörsmönstret kan du också tillämpa samma beteende på ett föremål så många gånger du vill. Om en kund beställer en pizza med allt på, och lite extra ost, kan du uppdatera huvudklassen med följande kod för att återspegla detta:

Pizza pizza2 = ny YeastCrustPizza();
pizza2 = ny Pepperoni (pizza2);
pizza2 = ny Ost (pizza2);
pizza2 = ny Ost (pizza2);
pizza2 = ny Svamp (pizza2);

System.out.println (pizza2.description() + " $" + pizza2.cost());

Den uppdaterade applikationen kommer att producera följande utdata i konsolen:

Fördelarna med att använda dekorationsmönster

De två stora fördelarna med att använda designmönstret för dekoration är säkerhet och flexibilitet. Dekorationsmönstret låter dig utveckla säkrare kod genom att inte störa redan existerande säkerhetskod. Den utökar istället befintlig kod genom komposition. Förhindrar effektivt introduktionen av nya buggar eller oavsiktliga biverkningar.

På grund av sammansättningen har en utvecklare också mycket flexibilitet när man använder dekorationsmönstret. Du kan implementera en ny dekoratör när som helst för att lägga till nytt beteende, utan att ändra befintlig kod och störa applikationen.