Am analizat deja utilizarea unui obiect singleton, dar este posibil să nu realizați încă că această strategie este un model de design și unul dintre cele mai utilizate în acest sens.

De fapt, există o mulțime de aceste modele și pot fi clasificate în funcție de scopul lor specific.

Clasificarea modelelor

Tipul de model Aplicație
Creativ Un tip care rezolvă problema creării obiectelor
Structural Modele care ne permit să construim o ierarhie de clasă corectă și extensibilă în arhitectura noastră
Comportamental Acest grup de modele facilitează interacțiunea sigură și convenabilă între obiectele dintr-un program.

De obicei, un model este caracterizat de problema pe care o rezolvă. Să aruncăm o privire la câteva modele pe care le întâlnim cel mai des atunci când lucrăm cu Java:

Model Scop
Singleton Suntem deja familiarizați cu acest model - îl folosim pentru a crea și accesa o clasă care nu poate avea mai mult de o instanță.
Iterator De asemenea, suntem familiarizați cu acesta. Știm că acest model ne permite să iterăm peste un obiect de colecție fără a dezvălui reprezentarea sa internă. Se folosește cu colecții.
Adaptor Acest model conectează obiecte incompatibile, astfel încât acestea să poată lucra împreună. Cred că numele modelului adaptorului vă ajută să vă imaginați exact ce face acesta. Iată un exemplu simplu din viața reală: un adaptor USB pentru o priză de perete.
Metoda șablonului

Un model de programare comportamentală care rezolvă problema integrării și vă permite să schimbați pașii algoritmici fără a modifica structura unui algoritm.

Imaginați-vă că avem un algoritm de asamblare a mașinii sub forma unei secvențe de pași de asamblare:

Șasiu -> Caroseria -> Motor -> Interior cabină

Dacă punem un cadru ranforsat, un motor mai puternic sau un interior cu iluminare suplimentară, nu trebuie să schimbăm algoritmul, iar secvența abstractă rămâne aceeași.

Decorator Acest model creează ambalaje pentru obiecte pentru a le oferi funcționalitate utilă. O vom considera ca parte a acestui articol.

În Java.io, următoarele clase implementează modele:

Model Unde este folosit în java.io
Adaptor
Metoda șablonului
Decorator

Model de decorator

Să ne imaginăm că descriem un model pentru amenajarea unei case.

În general, abordarea arată astfel:

Inițial, avem de ales dintre mai multe tipuri de case. Configurația minimă este un etaj cu acoperiș. Apoi folosim tot felul de decoratori pentru a schimba parametri suplimentari, ceea ce afectează în mod natural prețul casei.

Creăm o clasă abstractă House:

public abstract class House {
	String info;

	public String getInfo() {
    	return info;
	}

	public abstract int getPrice();
}

Aici avem 2 metode:

  • getInfo() returnează informații despre numele și caracteristicile casei noastre;
  • getPrice() returnează prețul configurației actuale a casei.

Avem, de asemenea, implementări standard pentru casă - cărămidă și lemn:

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;
	}
}

Ambele clase moștenesc clasa House și înlocuiesc metoda prețului acesteia, stabilind un preț personalizat pentru o casă standard. Setăm numele în constructor.

În continuare, trebuie să scriem cursuri de decoratori. Aceste clase vor moșteni și clasa House . Pentru a face acest lucru, creăm o clasă de decorator abstract.

Acolo vom pune o logică suplimentară pentru schimbarea unui obiect. Inițial, nu va exista o logică suplimentară și clasa abstractă va fi goală.

abstract class HouseDecorator extends House {
}

În continuare, creăm implementări de decorator. Vom crea mai multe clase care ne permit să adăugăm caracteristici suplimentare casei:

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";
	}
}
Un decorator care adaugă un al doilea etaj casei noastre

Constructorul decorator acceptă o casă pe care o vom „decor”, adică adăugăm modificări. Și suprascriem metodele getPrice() și getInfo() , returnând informații despre noua casă actualizată bazată pe cea veche.

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";
	}
}
Un decorator care adaugă un garaj casei noastre

Acum ne putem actualiza casa cu decoratori. Pentru a face acest lucru, trebuie să creăm o casă:

House brickHouse = new BrickHouse();

În continuare, ne-am setatcasavariabilă egală cu un nou decorator, care trece în casa noastră:

brickHouse = new SecondFloor(brickHouse);

Al nostrucasavariabila este acum o casa cu un al doilea etaj.

Să ne uităm la cazurile de utilizare care implică decoratori:

Exemplu de cod Ieșire
House brickHouse = new BrickHouse();

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

Casa de caramida

20000

House brickHouse = new BrickHouse();

  brickHouse = new SecondFloor(brickHouse);

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

Casa din caramida + etaj al doilea

40000

House brickHouse = new BrickHouse();


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

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

Casa din caramida + etaj al doilea + garaj

45000

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

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

Casa din lemn + garaj + etaj

50000

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

Casa de lemn

25000

Casa din lemn + garaj

30000

Acest exemplu ilustrează beneficiul actualizării unui obiect cu un decorator. Deci nu am schimbatcasa de lemnobiectul în sine, dar în schimb a creat un nou obiect bazat pe cel vechi. Aici putem observa că avantajele vin cu dezavantaje: creăm de fiecare dată un nou obiect în memorie, crescând consumul de memorie.

Priviți această diagramă UML a programului nostru:

Un decorator are o implementare super simplă și schimbă dinamic obiectele, actualizându-le. Decoratorii pot fi recunoscuți de constructorii lor, care iau ca parametri obiecte de același tip abstract sau interfață ca și clasa curentă. În Java, acest model este utilizat pe scară largă în clasele I/O.

De exemplu, așa cum am observat deja, toate subclasele java.io.InputStream , OutputStream , Reader și Writer au un constructor care acceptă obiecte din aceleași clase.