Vi har redan granskat användningen av ett singleton-objekt, men du kanske ännu inte inser att den här strategin är ett designmönster, och en av de mest använda på det.
Faktum är att det finns många av dessa mönster, och de kan klassificeras efter deras specifika syfte.
Mönsterklassificering
Mönstertyp | Ansökan |
---|---|
Skapande | En typ som löser problemet med att skapa objekt |
Strukturell | Mönster som låter oss bygga en korrekt och utbyggbar klasshierarki i vår arkitektur |
Beteende | Detta kluster av mönster underlättar säker och bekväm interaktion mellan objekt i ett program. |
Typiskt kännetecknas ett mönster av det problem det löser. Låt oss ta en titt på några mönster som vi stöter på oftast när vi arbetar med Java:
Mönster | Syfte |
---|---|
Singleton | Vi är redan bekanta med det här mönstret - vi använder det för att skapa och komma åt en klass som inte kan ha mer än en instans. |
Iterator | Vi är också bekanta med denna. Vi vet att detta mönster låter oss iterera över ett samlingsobjekt utan att avslöja dess interna representation. Den används med samlingar. |
Adapter | Detta mönster kopplar samman inkompatibla objekt så att de kan fungera tillsammans. Jag tror att namnet på adaptermönstret hjälper dig att föreställa dig exakt vad det gör. Här är ett enkelt exempel från verkligheten: en USB-adapter för ett vägguttag. |
Mall metod |
Ett beteendeprogrammeringsmönster som löser integrationsproblemet och låter dig ändra algoritmsteg utan att ändra strukturen på en algoritm. Föreställ dig att vi har en bilmonteringsalgoritm i form av en sekvens av monteringssteg: Chassi -> Kaross -> Motor -> Hyttinteriör Om vi sätter in en förstärkt ram, en kraftfullare motor eller en interiör med extra belysning, behöver vi inte ändra algoritmen, och den abstrakta sekvensen förblir densamma. |
Dekoratör | Detta mönster skapar omslag för objekt för att ge dem användbar funktionalitet. Vi kommer att betrakta det som en del av den här artikeln. |
I Java.io implementerar följande klasser mönster:
Mönster | Där det används i java.io |
---|---|
Adapter |
|
Mall metod | |
Dekoratör |
Dekoratörsmönster
Låt oss föreställa oss att vi beskriver en modell för en hemdesign.
Generellt sett ser tillvägagångssättet ut så här:
Inledningsvis har vi ett urval av flera typer av hus. Minsta konfiguration är en våning med tak. Sedan använder vi alla möjliga inredare för att ändra ytterligare parametrar, vilket naturligtvis påverkar priset på huset.
Vi skapar en abstrakt husklass:
public abstract class House {
String info;
public String getInfo() {
return info;
}
public abstract int getPrice();
}
Här har vi 2 metoder:
- getInfo() returnerar information om namnet och funktionerna i vårt hus;
- getPrice() returnerar priset för den aktuella huskonfigurationen.
Vi har också standardhusimplementeringar - tegel och trä:
public class BrickHouse extends House {
public BrickHouse() {
info = "Brick House";
}
@Override
public int getPrice() {
return 20_000;
}
}
public class WoodenHouse extends House {
public WoodenHouse() {
info = "Wooden House";
}
@Override
public int getPrice() {
return 25_000;
}
}
Båda klasserna ärver House- klassen och åsidosätter dess prismetod och sätter ett anpassat pris för ett standardhus. Vi anger namnet i konstruktorn.
Därefter måste vi skriva dekoratörsklasser. Dessa klasser kommer också att ärva husklassen . För att göra detta skapar vi en abstrakt dekoratörsklass.
Det är där vi lägger ytterligare logik för att ändra ett objekt. Inledningsvis kommer det inte att finnas någon ytterligare logik och den abstrakta klassen kommer att vara tom.
abstract class HouseDecorator extends House {
}
Därefter skapar vi dekoratörsimplementeringar. Vi kommer att skapa flera klasser som låter oss lägga till ytterligare funktioner till huset:
public class SecondFloor extends HouseDecorator {
House house;
public SecondFloor(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 20_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + second floor";
}
}
En dekoratör som lägger till en andra våning till vårt hus |
Inredarens konstruktör accepterar ett hus som vi ska "inreda", dvs lägga till modifieringar. Och vi åsidosätter metoderna getPrice() och getInfo() och returnerar information om det nya uppdaterade huset baserat på det gamla.
public class Garage extends HouseDecorator {
House house;
public Garage(House house) {
this.house = house;
}
@Override
public int getPrice() {
return house.getPrice() + 5_000;
}
@Override
public String getInfo() {
return house.getInfo() + " + garage";
}
}
En dekoratör som lägger till ett garage till vårt hus |
Nu kan vi uppdatera vårt hus med dekoratörer. För att göra detta måste vi skapa ett hus:
House brickHouse = new BrickHouse();
Därefter ställer vi in vårhusvariabel lika med en ny dekoratör som passerar i vårt hus:
brickHouse = new SecondFloor(brickHouse);
Vårhusvariabel är nu ett hus med en andra våning.
Låt oss titta på användningsfall som involverar dekoratörer:
Exempelkod | Produktion |
---|---|
|
Tegelhus 20 000 |
|
Tegelhus + andra våningen 40 000 |
|
Tegelhus + andra våningen + garage 45 000 |
|
Trähus + garage + andra våningen 50 000 |
|
Trähus 25 000 Trähus + garage 30 000 |
Detta exempel illustrerar fördelen med att uppgradera ett objekt med en dekoratör. Så vi ändrade inteträhusobjektet självt, utan skapade istället ett nytt objekt baserat på det gamla. Här kan vi se att fördelarna kommer med nackdelar: vi skapar ett nytt objekt i minnet varje gång, vilket ökar minnesförbrukningen.
Titta på detta UML-diagram av vårt program:
En dekoratör har en superenkel implementering och ändrar objekt dynamiskt och uppgraderar dem. Dekoratörer kan kännas igen av sina konstruktörer, som tar objekt av samma abstrakta typ eller gränssnitt som den aktuella klassen som parametrar. I Java används detta mönster flitigt i I/O-klasser.
Till exempel, som vi redan har noterat, har alla underklasser av java.io.InputStream , OutputStream , Reader och Writer en konstruktor som accepterar objekt av samma klasser.
GO TO FULL VERSION