CodeGym /Kursy /All lectures for PL purposes /Wzorce projektowe. Dekorator

Wzorce projektowe. Dekorator

All lectures for PL purposes
Poziom 1, Lekcja 39
Dostępny

Przeszliśmy już przez użycie obiektu klasy jako Singletona, ale możesz jeszcze nie podejrzewać, że jest to jeden z wzorców projektowych i jeden z najczęściej używanych.

W rzeczywistości istnieje wiele takich samych wzorców, ponadto mają one hierarchię, a każdy wzorzec ma swój własny cel.

Klasyfikacja wzorców

Typ wzoru Stosowalność
Generatywny Typ, który rozwiązuje problem tworzenia obiektów
Strukturalny Wzorce, które pozwalają nam zbudować poprawną, rozszerzalną hierarchię klas w naszej architekturze
behawioralne To skupisko wzorców odpowiada za bezpieczną i wygodną interakcję między obiektami programu.

Zazwyczaj wzorzec charakteryzuje się problemem, który rozwiązuje. Przyjrzyjmy się kilku wzorcom, z którymi najczęściej spotykamy się podczas pracy z Javą:

wzór zamiar
Singel Jesteśmy już z nim zaznajomieni i używamy go do tworzenia i odwoływania się do klasy, która nie może mieć więcej niż jednej instancji.
Iterator Nawiasem mówiąc, my również go znamy i wiemy, że ten wzorzec pozwala nam iterować po złożonym obiekcie bez ujawniania jego wewnętrznej reprezentacji. Używany z kolekcjami.
Adapter Łączy niekompatybilne obiekty, aby ze sobą współpracowały. Myślę, że mówiąc o adapterze, każdy wyobraża sobie dokładnie, co robi ten wzór. Prostym przykładem z życia jest adapter USB - wtyczka w gniazdku.
Metoda szablonowa

Wzorzec programowania behawioralnego, który rozwiązuje problem integracji i pozwala zmieniać jego kroki bez zmiany struktury algorytmu.

Wyobraź sobie, że mamy algorytm składania samochodu w postaci sekwencji składania:

Podwozie -> Nadwozie -> Silnik -> montaż wnętrza

Jeśli włożymy wzmocnioną ramę, mocniejszy silnik lub wnętrze z dodatkowym oświetleniem, nadal nie zmienimy algorytmu, a abstrakcyjna sekwencja pozostanie taka sama.

dekorator Tworzy opakowania dla obiektów, aby dodać do nich użyteczną funkcjonalność. Rozważymy to w ramach tego artykułu.

W Java.io wzorce są zaimplementowane w następujących klasach:

Dekorator wzorów

Wyobraźmy sobie, że opisujemy model projektu domu.

Ogólnie schemat będzie wyglądał następująco:

Początkowo mamy do wyboru kilka typów domków. Minimalna konfiguracja to jedno piętro z dachem, wtedy przy pomocy wszelkiego rodzaju dekoratorów możemy zmienić dodatkowe parametry, a to wpłynie na cenę domu.

Utwórz abstrakcyjną lekcję domową:


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

Tutaj mamy 2 metody:

  • getInfo() zwraca informację o nazwie i wyposażeniu naszego domu;
  • getPrice() - cena domu w aktualnej konfiguracji.

Posiadamy również standardowe realizacje domów - murowane i drewniane:


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

Obie klasy dziedziczą z klasy Dom i zastępują metodę ceny, ustawiając niestandardową cenę dla standardowego domu. Przypisz nazwę w konstruktorze.

Następnie musimy napisać klasy - dekoratorów. Są to klasy, które również dziedziczą po klasie House . W tym celu tworzymy abstrakcyjną klasę dekoratora.

W nim możemy umieścić dodatkową logikę zmiany obiektu. W naszym przypadku nie będzie dodatkowej logiki, a klasa abstrakcyjna będzie pusta.


abstract class HouseDecorator extends House {
}
    

Następnie tworzymy klasy implementacji dekoratora. Stworzymy kilka klas, które pozwolą nam dodać dodatkowe parametry do domu:


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";
	}
}
    
Dekorator, który dobuduje drugie piętro do naszego domu

Konstruktor dekoratora akceptuje dom, do którego stosujemy modyfikacje dekoratora. I nadpisujemy metody getPrice() i getInfo() , zwracając informacje o nowym zmodernizowanym domu na podstawie starego.


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";
	}
}
    
Dekorator, który dobuduje garaż do naszego domu

Teraz możemy odmienić nasz dom z dekoratorami. Aby to zrobić, musisz stworzyć dom:


House brickHouse = new BrickHouse();
    

Następnie przypisujemy do naszej zmiennejdomnowa wartość w postaci dekoratora, któremu przekazujemy nasz dom:


brickHouse = new SecondFloor(brickHouse); 
    

Nasza zmiennadomma już dom z drugim piętrem.

Spójrzmy na przypadki użycia dekoratora:

Przykład kodu Wniosek

House brickHouse = new BrickHouse(); 

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

Dom z cegieł

20000


House brickHouse = new BrickHouse(); 

  brickHouse = new SecondFloor(brickHouse); 

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

Dom murowany + drugie piętro

40000


House brickHouse = new BrickHouse();
 

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

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

Dom murowany + piętro + garaż

45000


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

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

Drewniany dom + garaż + drugie piętro

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

Drewniany dom

25000

Drewniany dom + garaż

30000

W tym przykładzie widzimy korzyści płynące z ulepszenia obiektu za pomocą dekoratora. Okazuje się, że nie zmieniliśmy samego obiektudrewniany domi na jego podstawie utworzyliśmy nowy obiekt. Ale dzięki tej przewadze możemy również rozważyć wady: za każdym razem tworzymy nowy obiekt w pamięci, co daje jej dodatkowe obciążenie.

Rozważ diagram UML naszego programu:

Dekorator jest zaimplementowany w bardzo prosty sposób i służy do dynamicznej zmiany obiektów i ich modernizacji. Dekorator może zostać rozpoznany przez konstruktory, które jako parametry przyjmują obiekty tego samego typu abstrakcyjnego lub interfejsu, co bieżąca klasa. W Javie ten wzorzec jest szeroko stosowany w klasach I/O.

Na przykład, jak już zauważyliśmy, wszystkie podklasy java.io.InputStream , OutputStream , Reader i Writer mają konstruktor, który akceptuje obiekty tych samych klas.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION