Vi har allerede gjennomgått bruken av et singleton-objekt, men du er kanskje ikke klar over at denne strategien er et designmønster, og en av de mest brukte.

Faktisk er det mange av disse mønstrene, og de kan klassifiseres i henhold til deres spesifikke formål.

Mønsterklassifisering

Mønstertype applikasjon
Kreativt En type som løser problemet med objektoppretting
Strukturell Mønstre som lar oss bygge et korrekt og utvidbart klassehierarki i arkitekturen vår
Atferdsmessig Denne klyngen av mønstre forenkler sikker og praktisk interaksjon mellom objekter i et program.

Vanligvis er et mønster preget av problemet det løser. La oss ta en titt på noen mønstre som vi møter oftest når vi jobber med Java:

Mønster Hensikt
Singleton Vi er allerede kjent med dette mønsteret - vi bruker det til å opprette og få tilgang til en klasse som ikke kan ha mer enn én forekomst.
Iterator Vi er også kjent med denne. Vi vet at dette mønsteret lar oss iterere over et samlingsobjekt uten å avsløre dens interne representasjon. Den brukes med samlinger.
Adapter Dette mønsteret kobler sammen inkompatible objekter slik at de kan fungere sammen. Jeg tror navnet på adaptermønsteret hjelper deg å forestille deg nøyaktig hva dette gjør. Her er et enkelt eksempel fra det virkelige liv: en USB-adapter for en stikkontakt.
Malmetode

Et atferdsprogrammeringsmønster som løser integrasjonsproblemet og lar deg endre algoritmiske trinn uten å endre strukturen til en algoritme.

Tenk deg at vi har en bilmonteringsalgoritme i form av en sekvens av monteringstrinn:

Chassis -> Karosseri -> Motor -> Hytteinteriør

Hvis vi setter inn en forsterket ramme, en kraftigere motor eller et interiør med ekstra belysning, trenger vi ikke å endre algoritmen, og den abstrakte sekvensen forblir den samme.

Dekoratør Dette mønsteret lager innpakninger for objekter for å gi dem nyttig funksjonalitet. Vi vil vurdere det som en del av denne artikkelen.

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

Dekorasjonsmønster

La oss forestille oss at vi beskriver en modell for et hjemdesign.

Generelt ser tilnærmingen slik ut:

I første omgang har vi valg mellom flere typer hus. Minimumskonfigurasjonen er én etasje med tak. Da bruker vi alle slags dekoratører for å endre tilleggsparametre, noe som naturligvis påvirker prisen på huset.

Vi lager en abstrakt husklasse:


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

Her har vi 2 metoder:

  • getInfo() returnerer informasjon om navnet og funksjonene til huset vårt;
  • getPrice() returnerer prisen på gjeldende huskonfigurasjon.

Vi har også standard husimplementeringer - murstein og tre:


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 klassene arver House -klassen og overstyrer dens prismetode, og setter en egendefinert pris for et standardhus. Vi setter navnet i konstruktøren.

Deretter må vi skrive dekoratørklasser. Disse klassene vil også arve Husklassen . For å gjøre dette lager vi en abstrakt dekoratørklasse.

Det er der vi legger inn ytterligere logikk for å endre et objekt. I utgangspunktet vil det ikke være noen ekstra logikk og abstraktklassen vil være tom.


abstract class HouseDecorator extends House {
}
    

Deretter lager vi dekorasjonsimplementeringer. Vi vil lage flere klasser som lar oss legge til flere funksjoner 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 som legger til en andre etasje til huset vårt

Dekoratørkonstruktøren aksepterer et hus som vi skal "pynte", dvs. legge til modifikasjoner. Og vi overstyrer metodene getPrice() og getInfo() , og returnerer informasjon om det nye oppdaterte huset basert 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 som legger til en garasje til huset vårt

Nå kan vi oppdatere huset vårt med dekoratører. For å gjøre dette må vi lage et hus:


House brickHouse = new BrickHouse();
    

Deretter setter vi vårhusvariabel lik en ny dekoratør, passerer i huset vårt:


brickHouse = new SecondFloor(brickHouse); 
    

Vårhusvariabel er nå et hus med en andre etasje.

La oss se på brukstilfeller som involverer dekoratører:

Eksempelkode Produksjon

House brickHouse = new BrickHouse(); 

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

Mursteinhus

20 000


House brickHouse = new BrickHouse(); 

  brickHouse = new SecondFloor(brickHouse); 

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

Mursteinhus + andre etasje

40 000


House brickHouse = new BrickHouse();
 

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

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

Mursteinshus + andre etasje + garasje

45 000


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

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

Trehus + garasje + andre etasje

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());
                    

Trehus

25 000

Trehus + garasje

30 000

Dette eksemplet illustrerer fordelen med å oppgradere et objekt med en dekoratør. Så vi endret ikketrehusobjektet selv, men laget i stedet et nytt objekt basert på det gamle. Her kan vi se at fordelene kommer med ulemper: vi lager et nytt objekt i minnet hver gang, noe som øker minneforbruket.

Se på dette UML-diagrammet av programmet vårt:

En dekoratør har en superenkel implementering og endrer objekter dynamisk ved å oppgradere dem. Dekoratører kan gjenkjennes av konstruktørene deres, som tar objekter av samme abstrakte type eller grensesnitt som parametere som den gjeldende klassen. I Java er dette mønsteret mye brukt i I/O-klasser.

For eksempel, som vi allerede har bemerket, har alle underklasser av java.io.InputStream , OutputStream , Reader og Writer en konstruktør som godtar objekter av de samme klassene.