Et av de viktigste prinsippene i programvareutvikling er åpent-lukket designprinsipp. Dette designprinsippet understreker at klasser skal være åpne for utvidelse, men stengt for modifikasjon. Dekoratørens designmønster legemliggjør det åpne-lukkede designprinsippet.
Med dekoratørdesignmønsteret kan du enkelt utvide en klasse ved å gi den ny oppførsel uten å endre den eksisterende koden. Dekorasjonsmønsteret gjør dette dynamisk under kjøring, ved hjelp av komposisjon. Dette designmønsteret er kjent som et fleksibelt alternativ til å bruke arv for å utvide atferd.
Hvordan fungerer dekorasjonsmønsteret?
Selv om dekoratørmønsteret er et alternativ til klassearv, innlemmer det noen aspekter av arv i designet. Et sentralt aspekt ved dekorasjonsmønsteret er at alle klassene er relatert, enten direkte eller indirekte.
Et typisk dekoratørdesignmønster har følgende struktur:
Fra klassediagrammet ovenfor kan du se at dekorasjonsmønsteret har fire hovedklasser.
Komponent: dette er en abstrakt klasse (eller grensesnitt), som fungerer som supertypen for dekorasjonsmønsteret.
Betongkomponent: dette er gjenstandene du kan dekorere med forskjellig oppførsel under kjøring. De arver fra komponentgrensesnittet og implementerer abstrakte funksjoner.
Dekoratør: denne klassen er abstrakt og har samme supertype som objektet den skal dekorere. I klassediagrammet vil du se to forhold mellom komponent- og dekoratørklassene. Det første forholdet er et av arv; hver dekoratør er en komponent. Det andre forholdet er komposisjon; hver dekoratør har en (eller omslutter en) komponent.
Betongdekorator: dette er de individuelle dekoratørene som gir en komponent en bestemt oppførsel. Du bør merke deg at hver betongdekorator har en instansvariabel som inneholder en referanse til en komponent.
Implementering av Decorator Design Pattern i Java
En prøveapplikasjon for pizzabestilling kan demonstrere hvordan du bruker dekorasjonsmønsteret til å utvikle applikasjoner. Denne prøvepizzaapplikasjonen lar kunder bestille pizza med flere pålegg. Den første klassen av dekorasjonsmønsteret er pizzagrensesnittet:
offentliggrensesnittPizza{
offentligabstrakt String beskrivelse();
offentligabstraktdobbeltkoste();
}
Pizza-grensesnittet er komponentklassen. Så du kan lage en eller flere konkrete klasser fra den. Pizzaselskapet lager to hovedtyper pizza, basert på deigen deres. En type pizza har gjærdeig:
offentligklasseYeastCrustPizzaredskaperPizza{
@Overstyring
offentlig String beskrivelse(){
komme tilbake"Pizzadeig laget med gjær";
}
@Overstyring
offentligdobbeltkoste(){
komme tilbake18.00;
}
}
YeastCrustPizzaen er den første betongen Java klasse av Pizza-grensesnittet. Den andre typen pizza som er tilgjengelig er flatbrød:
offentligklasseFlatbreadCrustPizzaredskaperPizza{
@Overstyring
offentlig String beskrivelse(){
komme tilbake"Pizzadeig laget med flatbrød";
}
@Overstyring
offentligdobbeltkoste(){
komme tilbake15.00;
}
}
FlatbreadCrustPizza-klassen er den andre konkrete komponenten, og i likhet med YeastCrustPizza-klassen implementerer den alle de abstrakte funksjonene til Pizza-grensesnittet.
Dekoratørene
Dekoratørklassen er alltid abstrakt, så du kan ikke lage en ny instans direkte fra den. Men det er nødvendig å etablere et forhold mellom de forskjellige dekoratørene og komponentene som de skal dekorere.
offentligabstraktklasseToppingDecoratorredskaperPizza{
offentlig String beskrivelse(){
komme tilbake"Ukjent topping";
}
}
ToppingDecorator-klassen representerer dekoratørklassen i denne prøveapplikasjonen. Nå kan pizzaselskapet lage mange forskjellige pålegg (eller dekoratører), ved å bruke ToppingDecorator-klassen. La oss si at en pizza kan ha tre forskjellige typer pålegg, nemlig ost, pepperoni og sopp.
Ostetopping
offentligklasseOststrekkerToppingDecorator{
privat Pizza pizza;offentligOst(Pizza pizza){
dette.pizza = pizza;
}@Overstyring
offentlig String beskrivelse(){
komme tilbake pizza.description() + ", ostetopping";
}
@Overstyring
offentligdobbeltkoste(){
komme tilbakepizza.koste() + 2.50;
}
}
Pepperoni topping
offentligklassePepperonistrekkerToppingDecorator{
privat Pizza pizza;offentligPepperoni(Pizza pizza){
dette.pizza = pizza;
}@Overstyring
offentlig String beskrivelse(){
komme tilbake pizza.description() + ", Pepperoni Topping";
}
@Overstyring
offentligdobbeltkoste(){
komme tilbakepizza.koste() + 3.50;
}
}
Sopptopping
offentligklasseSoppstrekkerToppingDecorator{
privat Pizza pizza;offentligSopp(Pizza pizza){
dette.pizza = pizza;
}
@Overstyring
offentlig String beskrivelse(){
komme tilbake pizza.description() + ", sopptopping";
}
@Overstyring
offentligdobbeltkoste(){
komme tilbakepizza.koste() + 4.50;
}
}
Nå har du en enkel applikasjon implementert ved hjelp av dekorasjonsmønsteret. Hvis en kunde skulle bestille en pizza med gjærskorpe med ost og pepperoni, vil testkoden for det scenariet se ut som følger:
offentligklasseHoved{
offentligstatisktomromhoved-(String[] args){
Pizza pizza1 = ny YeastCrustPizza();
pizza1 = ny Pepperoni (pizza1);
pizza1 = ny Ost (pizza1);
System.out.println (pizza1.description() + " $" + pizza1.cost());
}
}
Å kjøre denne koden vil produsere følgende utgang i konsollen:
Som du kan se, angir utgangen typen pizza sammen med totalkostnaden. Pizzaen startet som en pizza med gjærskorpe for $18,00, men med dekorasjonsmønsteret kunne applikasjonen legge til nye funksjoner og deres passende pris til pizzaen. Dermed gir pizzaen ny oppførsel uten å endre den eksisterende koden (gjærskorpepizzaen).
Med dekorasjonsmønsteret kan du også bruke samme oppførsel på en gjenstand så mange ganger du ønsker. Hvis en kunde bestiller en pizza med alt på, og litt ekstra ost, kan du oppdatere hovedklassen med følgende kode for å gjenspeile dette:
Pizza pizza2 = ny YeastCrustPizza();
pizza2 = ny Pepperoni (pizza2);
pizza2 = ny ost (pizza2);
pizza2 = ny ost (pizza2);
pizza2 = ny sopp (pizza2);System.out.println (pizza2.description() + " $" + pizza2.cost());
Den oppdaterte applikasjonen vil produsere følgende utdata i konsollen:
Fordelene ved å bruke dekorasjonsmønsteret
De to store fordelene med å bruke dekorasjonsmønsteret er sikkerhet og fleksibilitet. Dekorasjonsmønsteret lar deg utvikle sikrere kode ved ikke å forstyrre eksisterende sikkerhetskode. Den utvider i stedet eksisterende kode gjennom komposisjon. Forhindrer effektivt introduksjonen av nye feil eller utilsiktede bivirkninger.
På grunn av komposisjonen har en utvikler også mye fleksibilitet ved bruk av dekorasjonsmønsteret. Du kan implementere en ny dekoratør når som helst for å legge til ny atferd, uten å endre eksisterende kode og forstyrre applikasjonen.