Prototyp jest jednym z kreacyjnych wzorców projektowych. W porównaniu do wzorca jakim jest Fabryka, wykazuje pewne podobieństwa. Generalną różnicą jest jednak fakt potrzeby otrzymania dokładnych kopii obiektów wraz z wartościami pól danych, stanów itp…

Z zasady tworząc wzorzec Prototyp, tworzy się go jako interfejs z wymogiem zaimplementowania metody clone(); służącej do klonowania obiektu wraz z jego danymi do następnych instancji. Jednym z powodów chęci użycia tego wzorca może być np. opisywanie struktury kolonii bakterii lub mrówek (ACO), gdzie każdemu z atomowych bytów przypiszemy obiekt zawierający wszystkie niezbędne dane umożliwiające nadanie pewnego początkowego statusu tego bytu.

Najprościej, kopiując obiekt byłoby po prostu stworzyć nowy obiekt tej samej klasy, a następnie skopiować dane pól do nowego obiektu. Czasem może to być trudne lub wręcz niewykonalne, gdy niektóre pola lub metody będą posiadać selektor private/protected czyli będą niedostępne z zewnętrza obiektu. Dlatego też implementacja metody klonującej wewnątrz takiej klasy jest krytycznie istotna. Nie rozwiąże to jednak wszystkich problemów, bowiem w przypadku gdy obiekt zawiera odwołania danych referencyjnych to nadal pozostaje kwestia jak je potraktować tzn. czy nowy obiekt powinien posiadać również sklonowane te same dane, czy referencje powinny pozostać niezmienne. Decyzje co do tego jak klonować obiekt muszą być zatem podjęte w trakcie konstruowania klasy. Zatem we wzorcu projektowym Prototyp proces klonowania jest delegowany tym obiektom, które mają zostać sklonowane. We wzorcu deklarowany jest wspólny interfejs dla wszystkich obiektów mających wspierać funkcjonalność klonowania. Interfejs ten umożliwia proces klonowania obiektów bez konieczności powiązania kodu z klasą obiektu. Reasumując, obiekt posiadający funkcjonalność klonowania staje się prototypem, z którego można wywieść setki innych. Tak, dobrze zauważyłeś, zazwyczaj wykorzystujemy tutaj polimorfizm.

Użycie…

Użycie wzorca Prototypu może wyglądać następująco:

package wzorceprojektowe1;

/**
 *
 * @author Utracki
 */
public abstract class PrototypeService {

enum kolor {czarny,biały,czerwony,zielony,enigmatyczny}    
int x, y;
kolor k;

public PrototypeService() {

    }

public PrototypeService(PrototypeService ps) {
        this();
        this.x = ps.x;
        this.y = ps.y;
        this.k = ps.k;
    }
    

@Override
public abstract PrototypeService clone();
   
}

class Kwadrat extends PrototypeService {
    int bok;
    
    Kwadrat(int bok) {
        super();
        this.bok=bok;
    }
    
    Kwadrat(Kwadrat źródło) {
        super();
        bok=źródło.bok;
    }
    
    Kwadrat clone(Kwadrat kw01) {
       return new Kwadrat(this);
    }

    @Override
    public PrototypeService clone() {
        return new Kwadrat(this);
    }
       
}

A teraz zobaczmy jak prosto stworzyć prototyp i zacząć go “mnożyć” w praktyce w programie:

    Kwadrat kw01 = new Kwadrat(10);    
    Kwadrat kw02 = kw01.clone(kw01);
   
    System.out.println(kw02.bok);

Nieco bardziej skomplikowany przykład poniżej, gdzie korzystamy również z polimorfizmu dla utworzenia struktury 100-elementowej. Wpierw modyfikujemy założenia projektowe klas…

package wzorceprojektowe1;

/**
 *
 * @author Utracki
 */
public abstract class PrototypeService {

enum kolor {czarny,biały,czerwony,zielony,enigmatyczny}    
int x, y;
kolor k;

public PrototypeService() {    }

public PrototypeService(PrototypeService ps) {
        this();
        this.x = ps.x;
        this.y = ps.y;
        this.k = ps.k;
    }
    
public abstract PrototypeService clone(PrototypeService ps);

@Override
public abstract String toString();
   
}


class Kwadrat extends PrototypeService {
    int bok;
    
    Kwadrat() {
        super();
    }
    
    Kwadrat(Kwadrat źródło) {
        super(źródło);
        bok=źródło.bok;
    }
    


    @Override
    public PrototypeService clone(PrototypeService ps) {
        Kwadrat kw=new Kwadrat(this);
        return kw;
    }

    @Override
    public String toString() {
        return Integer.toString(this.bok);
    }
    
    
}    
    
 class Koło extends PrototypeService {
    int promień;
    
    Koło() {
        super();
    }
    
    Koło(Koło źródło) {
        super(źródło);
        promień=źródło.promień;
    }
    
    @Override
    public PrototypeService clone(PrototypeService ps) {
       return new Koło(this);
    }   

    @Override
    public String toString() {  // nie definiuję tej metody już z "lenistwa" ;-)
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    
}

A teraz bardziej skomplikujmy program reprezentujący kilka przykładowych odwołań (tworzenie instancji) do zdefiniowanych wyżej klas…

    PrototypeService [] tablicaFigur=new PrototypeService[100];
    
    Kwadrat kw01 = new Kwadrat();
    kw01.bok=10;
    kw01.k=kolor.biały;
    kw01.x=100;
    kw01.y=150; //pozycja x i y lewego górnego rogu...
    
    Kwadrat kw02 = (Kwadrat) kw01.clone(kw01);
    
    Koło ko01 = new Koło();
    
    tablicaFigur[0]=kw01;
    tablicaFigur[1]=kw02;
    tablicaFigur[2]=ko01;
    
    
    System.out.println(kw01.bok+" kolor: "+kw01.k);
    System.out.println(kw02.bok+" kolor: "+kw02.k);
    
    System.out.println(tablicaFigur[1].toString()+" kolor: "+kw02.k);  //przykład użycia polimorfizmu i korzystania z definiowanych później w hierarchii pól danych.
    

W rezultacie otrzymaliśmy obiekty zawierające niezbędne informacje o oczekiwanym rezultacie i przygotowane w czytelny, prosty do rozbudowy sposób.

Zadanie: Przygotuj prostą demonstrację z wykorzystaniem wzorca, gdzie w przykładowym mrowisku każda mrówka będzie posiadać informację o jej sile (umożliwiającej uniesienie ciężaru uzależnionego od wieku mrówki), wieku, deklarowanym czasie życia, oraz stopnia jej niezależności (indywidualności).

Przygotuj program, który będzie pokazywał rozwój mrowiska w czasie jego zdolność kreacyjną (siła mrówek) i zestawienie populacyjne takiego mrowiska (do określenia wzrostów i spadków populacyjnych użyj wskaźnika losowego).

Rozważ inny przypadek – gdy tworzysz populację studentów i nauczycieli w klasie… co zauważasz?? Czy klasyczny Prototyp spełni się tutaj??