Wzorzec prototypu to jeden z kluczowych wzorców projektowych, który pozwala na kopiowanie istniejących obiektów bez tworzenia zależności pomiędzy kodem a klasami tych obiektów. Jego zastosowanie jest szczególnie korzystne, gdy chcemy dynamicznie tworzyć obiekty bazujące na istniejących instancjach, unikając jednocześnie kosztów związanych z ich pełnym inicjalizowaniem. W tym artykule omówimy wzorzec prototypu, jego zalety oraz pokażemy, jak go używać na przykładzie klonowania obiektów Robot.
Czym jest wzorzec prototypu?
Wzorzec prototypu umożliwia klonowanie już istniejących obiektów poprzez delegowanie procesu klonowania samym obiektom. Pozwala to na uniknięcie uzależniania kodu od konkretnych klas, przez co staje się on bardziej elastyczny. Mechanizm klonowania jest wbudowany w same obiekty, co pozwala na głębokie lub płytkie kopiowanie ich stanu.
Korzyści z używania wzorca prototypu
- Niezależność od klas: Kod klonujący obiekty nie musi znać konkretnej klasy obiektu, który chce skopiować.
- Oszczędność zasobów: Klonowanie jest często tańsze pod względem wydajności niż tworzenie nowego obiektu od podstaw.
- Elastyczność: Możliwość dynamicznego tworzenia nowego obiektu na podstawie istniejącej instancji i dostosowywania go do wymagań.
Implementacja wzorca prototypu
Aby zobrazować, jak działa wzorzec prototypu, posłużmy się przykładem klonowania obiektów Robot. Załóżmy, że każdy Robot ma swoje identyczne cechy, które mają być kopiowane do nowo tworzonego obiektu.
Definicja klasy Robot
Na początku definiujemy klasę Robot, która implementuje interfejs Cloneable. Posiada ona pola takie jak model i serialNumber, oraz implementację metody clone():
public class Robot implements Cloneable {
private String model;
private int serialNumber;public Robot(String model, int serialNumber) {
this.model = model;
this.serialNumber = serialNumber;
}@Override
public Robot clone() {
try {
return (Robot) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Cloning not supported", e);
}
}@Override
public String toString() {
return "Robot [model=" + model + ", serialNumber=" + serialNumber + "]";
}
}
Użycie wzorca prototypu
Poniżej przedstawiamy, jak klonować robota za pomocą wzorca prototypu:
public class PrototypeDemo {
public static void main(String[] args) {
Robot originalRobot = new Robot("T-800", 101);
Robot clonedRobot = originalRobot.clone();System.out.println(originalRobot);
System.out.println(clonedRobot);
}
}
Więcej na temat płytkiego i głębokiego klonowania
W powyższym przykładzie klonowanie odbywa się za pomocą domyślnego mechanizmu Java, co oznacza płytkie klonowanie. Dla większości prostych przypadków jest to wystarczające, jednak w bardziej złożonych scenariuszach, gdzie obiekty zawierają zagnieżdżone struktury, może być potrzebne głębokie klonowanie.
Oto przykład klasy Robot, która posiada dodatkowo zagnieżdżony obiekt Component oraz jego głębokie klonowanie:
Klasa Component i jej włączenie do Robot
public class Component implements Cloneable {
private String name;public Component(String name) {
this.name = name;
}@Override
public Component clone() {
try {
return (Component) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Cloning not supported", e);
}
}@Override
public String toString() {
return "Component [name=" + name + "]";
}
}public class Robot implements Cloneable {
private String model;
private int serialNumber;
private Component component;public Robot(String model, int serialNumber, Component component) {
this.model = model;
this.serialNumber = serialNumber;
this.component = component;
}@Override
public Robot clone() {
try {
Robot cloned = (Robot) super.clone();
cloned.component = component.clone(); // Głębokie klonowanie komponentu
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Cloning not supported", e);
}
}@Override
public String toString() {
return "Robot [model=" + model + ", serialNumber=" + serialNumber + ", component=" + component + "]";
}
}
Użycie głębokiego klonowania
public class PrototypeDemo {
public static void main(String[] args) {
Component component = new Component("Laser");
Robot originalRobot = new Robot("T-800", 101, component);
Robot clonedRobot = originalRobot.clone();System.out.println(originalRobot);
System.out.println(clonedRobot);
}
}
W ten sposób wzorzec prototypu pozwala na klonowanie obiektów Robot bez tworzenia nowych zależności między kodem a klasami, zapewniając elastyczność i efektywność w zarządzaniu obiektami.