Et af de vigtigste principper i softwareudvikling er åben-lukket designprincippet. Dette designprincip understreger, at klasser skal være åbne for forlængelse, men lukket for modifikation. Dekoratørens designmønster inkarnerer det åbne-lukkede designprincip.
Med dekoratørens designmønster kan du nemt udvide en klasse ved at give den ny adfærd uden at ændre dens eksisterende kode. Dekoratørmønsteret gør dette dynamisk under kørsel ved hjælp af komposition. Dette designmønster er kendt som et fleksibelt alternativ til at bruge arv til at udvide adfærd.
Hvordan fungerer dekoratørens designmønster?
Selvom dekorationsmønsteret er et alternativ til klasses arv, det inkorporerer nogle aspekter af arv i sit design. Et centralt aspekt af dekoratørmønsteret er, at alle dets klasser er relaterede, enten direkte eller indirekte.
Et typisk dekoratørdesignmønster har følgende struktur:
Fra klassediagrammet ovenfor kan du se, at dekoratørmønsteret har fire hovedklasser.
Komponent: dette er en abstrakt klasse (eller grænseflade), der fungerer som supertype for dekorationsmønstret.
Betonkomponent: det er de genstande, som du kan dekorere med forskellig adfærd under kørsel. De arver fra komponentgrænsefladen og implementerer dens abstrakte funktioner.
Dekoratør: denne klasse er abstrakt og har samme supertype som den genstand, den vil dekorere. I klassediagrammet kan du se to forhold mellem komponent- og dekoratørklasserne. Det første forhold er et af arv; hver dekoratør er en komponent. Det andet forhold er komposition; hver dekoratør har en (eller omslutter en) komponent.
Betondekorator: det er de individuelle dekoratører, der giver en komponent en bestemt adfærd. Du skal bemærke, at hver betondekoratør har en instansvariabel, der indeholder en reference til en komponent.
Implementering af Decorator Design Pattern i Java
En prøveapplikation til pizzabestilling kan på passende vis demonstrere, hvordan man bruger dekorationsmønsteret til at udvikle applikationer. Denne prøvepizzaapplikation giver kunderne mulighed for at bestille pizzaer med flere toppings. Den første klasse af dekorationsmønstret er pizzagrænsefladen:
offentliginterfacePizza{
offentligabstrakt Snor beskrivelse();
offentligabstraktdobbeltkoste();
}
Pizza-grænsefladen er komponentklassen. Så du kan oprette en eller flere konkrete klasser ud fra det. Pizzafirmaet laver to hovedtyper af pizzaer, baseret på deres dej. En type pizza har gærdej:
offentligklasseYeastCrustPizzaredskaberPizza{
@Tilsidesæt
offentlig Snor beskrivelse(){
Vend tilbage"Pizzadej lavet med gær";
}
@Tilsidesæt
offentligdobbeltkoste(){
Vend tilbage18.00;
}
}
YeastCrustPizzaen er den første beton Java klasse af Pizza-grænsefladen. Den anden type pizza, der findes, er fladbrød:
offentligklasseFladbrødCrustPizzaredskaberPizza{
@Tilsidesæt
offentlig Snor beskrivelse(){
Vend tilbage"Pizzadej lavet med fladbrød";
}
@Tilsidesæt
offentligdobbeltkoste(){
Vend tilbage15.00;
}
}
FlatbreadCrustPizza-klassen er den anden konkrete komponent, og ligesom YeastCrustPizza-klassen implementerer den alle de abstrakte funktioner i Pizza-grænsefladen.
Dekoratørerne
Dekoratørklassen er altid abstrakt, så du kan ikke oprette en ny instans direkte fra den. Men det er nødvendigt at etablere et forhold mellem de forskellige dekoratører og de komponenter, som de skal dekorere.
offentligabstraktklasseToppingDecoratorredskaberPizza{
offentlig Snor beskrivelse(){
Vend tilbage"Ukendt topping";
}
}
ToppingDecorator-klassen repræsenterer dekoratørklassen i denne eksempelapplikation. Nu kan pizzafirmaet lave mange forskellige toppings (eller dekoratører) ved hjælp af ToppingDecorator-klassen. Lad os sige, at en pizza kan have tre forskellige typer toppings, nemlig ost, pepperoni og champignon.
Ost Topping
offentligklasseOststrækker sigToppingDecorator{
privat Pizza pizza;offentligOst(Pizza pizza){
det her.pizza = pizza;
}@Tilsidesæt
offentlig Snor beskrivelse(){
Vend tilbage pizza.description() + ", ostetopping";
}
@Tilsidesæt
offentligdobbeltkoste(){
Vend tilbagepizza.koste() + 2.50;
}
}
Pepperoni topping
offentligklassePepperonistrækker sigToppingDecorator{
privat Pizza pizza;offentligPepperoni(Pizza pizza){
det her.pizza = pizza;
}@Tilsidesæt
offentlig Snor beskrivelse(){
Vend tilbage pizza.description() + ", Pepperoni Topping";
}
@Tilsidesæt
offentligdobbeltkoste(){
Vend tilbagepizza.koste() + 3.50;
}
}
Svampetopping
offentligklasseChampignonstrækker sigToppingDecorator{
privat Pizza pizza;offentligChampignon(Pizza pizza){
det her.pizza = pizza;
}
@Tilsidesæt
offentlig Snor beskrivelse(){
Vend tilbage pizza.description() + ", svampetopping";
}
@Tilsidesæt
offentligdobbeltkoste(){
Vend tilbagepizza.koste() + 4.50;
}
}
Nu har du en simpel applikation implementeret ved hjælp af dekoratørens designmønster. Hvis en kunde skulle bestille en pizza med gærskorpe med ost og pepperoni, vil testkoden for det scenarie se ud som følger:
offentligklasseHoved{
offentligstatiskugyldigvigtigste(String[] args){
Pizza pizza1 = ny YeastCrustPizza();
pizza1 = ny Pepperoni (pizza1);
pizza1 = ny Ost (pizza1);
System.out.println (pizza1.description() + " $" + pizza1.cost());
}
}
Kørsel af denne kode produceres følgende output i konsollen:
Som du kan se, angiver outputtet typen af pizza sammen med dens samlede pris. Pizzaen startede som en gærskorpepizza til $18,00, men med dekorationsmønsteret var applikationen i stand til at tilføje nye funktioner og deres passende pris til pizzaen. Således giver pizzaen ny adfærd uden at ændre den eksisterende kode (gærskorpspizzaen).
Med dekorationsmønsteret kan du også anvende den samme adfærd på en genstand så mange gange, du ønsker. Hvis en kunde bestiller en pizza med alt på, og lidt ekstra ost, kan du opdatere hovedklassen med følgende kode for at afspejle dette:
Pizza pizza2 = ny YeastCrustPizza();
pizza2 = ny Pepperoni (pizza2);
pizza2 = ny Ost (pizza2);
pizza2 = ny Ost (pizza2);
pizza2 = ny Svampe (pizza2);System.out.println (pizza2.description() + " $" + pizza2.cost());
Den opdaterede applikation vil producere følgende output i konsollen:
Fordelene ved at bruge dekorationsmønsteret
De to store fordele ved at bruge dekoratørens designmønster er sikkerhed og fleksibilitet. Dekorationsmønsteret giver dig mulighed for at udvikle mere sikker kode ved ikke at forstyrre allerede eksisterende sikker kode. Det udvider i stedet eksisterende kode gennem sammensætning. Forebygger effektivt introduktionen af nye fejl eller utilsigtede bivirkninger.
På grund af sammensætningen har en udvikler også stor fleksibilitet ved brug af dekorationsmønsteret. Du kan implementere en ny dekoratør til enhver tid for at tilføje ny adfærd uden at ændre eksisterende kode og forstyrre applikationen.