Vi har allerede gennemgået brugen af ​​et singleton-objekt, men du er måske endnu ikke klar over, at denne strategi er et designmønster, og en af ​​de mest brugte.

Faktisk er der mange af disse mønstre, og de kan klassificeres efter deres specifikke formål.

Mønsterklassificering

Mønster type Ansøgning
Kreativt En type, der løser problemet med oprettelse af objekter
Strukturel Mønstre, der lader os opbygge et korrekt og udvideligt klassehierarki i vores arkitektur
Adfærdsmæssigt Denne klynge af mønstre letter sikker og bekvem interaktion mellem objekter i et program.

Typisk er et mønster karakteriseret ved det problem, det løser. Lad os tage et kig på et par mønstre, som vi oftest støder på, når vi arbejder med Java:

Mønster Formål
Singleton Vi er allerede bekendt med dette mønster - vi bruger det til at oprette og få adgang til en klasse, der ikke kan have mere end én forekomst.
Iterator Vi er også bekendt med denne. Vi ved, at dette mønster lader os iterere over et samlingsobjekt uden at afsløre dets interne repræsentation. Det bruges sammen med samlinger.
Adapter Dette mønster forbinder inkompatible objekter, så de kan arbejde sammen. Jeg tror, ​​at navnet på adaptermønsteret hjælper dig med at forestille dig præcis, hvad det gør. Her er et simpelt eksempel fra det virkelige liv: en USB-adapter til en stikkontakt.
Skabelon metode

Et adfærdsmæssigt programmeringsmønster, der løser integrationsproblemet og giver dig mulighed for at ændre algoritmiske trin uden at ændre strukturen af ​​en algoritme.

Forestil dig, at vi har en bilsamlingsalgoritme i form af en sekvens af monteringstrin:

Chassis -> Karosseri -> Motor -> Kabineinteriør

Hvis vi indsætter en forstærket ramme, en mere kraftfuld motor eller et interiør med ekstra belysning, behøver vi ikke at ændre algoritmen, og den abstrakte sekvens forbliver den samme.

Dekoratør Dette mønster skaber omslag til objekter for at give dem nyttig funktionalitet. Vi vil overveje det som en del af denne artikel.

I Java.io implementerer følgende klasser mønstre:

Mønster Hvor det bruges i java.io
Adapter
Skabelon metode
Dekoratør

Dekoratør mønster

Lad os forestille os, at vi beskriver en model til et boligdesign.

Generelt ser fremgangsmåden således ud:

I første omgang har vi valg mellem flere typer huse. Minimumskonfigurationen er en etage med tag. Så bruger vi alle mulige dekoratører til at ændre yderligere parametre, hvilket naturligvis påvirker husets pris.

Vi laver en abstrakt House-klasse:


public abstract class House {
	String info;
 
	public String getInfo() {
    	return info;
	}
 
	public abstract int getPrice();
}
    

Her har vi 2 metoder:

  • getInfo() returnerer information om navnet og funktionerne i vores hus;
  • getPrice() returnerer prisen for den aktuelle huskonfiguration.

Vi har også standard husimplementeringer - mursten og 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;
	}
}
    

Begge klasser arver House- klassen og tilsidesætter dens prismetode og sætter en tilpasset pris for et standardhus. Vi sætter navnet i konstruktøren.

Dernæst skal vi skrive dekoratørklasser. Disse klasser vil også arve House- klassen. For at gøre dette opretter vi en abstrakt dekoratørklasse.

Det er her, vi vil lægge yderligere logik til at ændre et objekt. I første omgang vil der ikke være nogen yderligere logik, og den abstrakte klasse vil være tom.


abstract class HouseDecorator extends House {
}
    

Dernæst laver vi dekorationsimplementeringer. Vi vil oprette flere klasser, der lader os tilføje yderligere funktioner til 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, der tilføjer en anden sal til vores hus

Dekoratør-konstruktøren accepterer et hus, som vi vil "indrette", dvs. tilføje ændringer. Og vi tilsidesætter metoderne getPrice() og getInfo() og returnerer oplysninger om det nye opdaterede hus baseret på det gamle.


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, der tilføjer en garage til vores hus

Nu kan vi opdatere vores hus med dekoratører. For at gøre dette skal vi lave et hus:


House brickHouse = new BrickHouse();
    

Dernæst sætter vi voreshusvariabel lig med en ny dekoratør, der passerer i vores hus:


brickHouse = new SecondFloor(brickHouse); 
    

Voreshusvariabel er nu et hus med en anden sal.

Lad os se på use cases, der involverer dekoratører:

Eksempel kode Produktion

House brickHouse = new BrickHouse(); 

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());
                    

Murstenshus

20.000


House brickHouse = new BrickHouse(); 

  brickHouse = new SecondFloor(brickHouse); 

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());
                    

Murstenshus + anden sal

40.000


House brickHouse = new BrickHouse();
 

  brickHouse = new SecondFloor(brickHouse);
  brickHouse = new Garage(brickHouse);

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());
                    

Murstenshus + anden sal + garage

45.000


House woodenHouse = new SecondFloor(new Garage(new WoodenHouse())); 

  System.out.println(woodenHouse.getInfo());
  System.out.println(woodenHouse.getPrice());
                    

Træhus + garage + anden sal

50.000


House woodenHouse = new WoodenHouse(); 

  House woodenHouseWithGarage = new Garage(woodenHouse);

  System.out.println(woodenHouse.getInfo());
  System.out.println(woodenHouse.getPrice());

  System.out.println(woodenHouseWithGarage.getInfo());
  System.out.println(woodenHouseWithGarage.getPrice());
                    

Træhus

25.000

Træhus + garage

30.000

Dette eksempel illustrerer fordelen ved at opgradere et objekt med en dekoratør. Så vi ændrede ikketræhusobjektet selv, men skabte i stedet et nyt objekt baseret på det gamle. Her kan vi se, at fordelene kommer med ulemper: vi skaber et nyt objekt i hukommelsen hver gang, hvilket øger hukommelsesforbruget.

Se dette UML-diagram over vores program:

En dekoratør har en super enkel implementering og ændrer objekter dynamisk og opgraderer dem. Dekoratører kan genkendes af deres konstruktører, som tager objekter af samme abstrakte type eller grænseflade som parametre som den aktuelle klasse. I Java er dette mønster meget brugt i I/O-klasser.

For eksempel, som vi allerede har bemærket, har alle underklasser af java.io.InputStream , OutputStream , Reader og Writer en konstruktør, der accepterer objekter af de samme klasser.