Implementacja wzorca projektowego Prototyp w Swift

W celu lepszego zapoznania się z tematem lepiej jest otworzyć plik playground w Xcode. Jest w nim zawarta ta sama treść, a dodatkowo można od razu zmieniać kod. Plik SwiftPrototypePattern.playground

Zastosowanie

Wzorzec projektowy prototyp (Prototype Pattern) tworzy nowy obiekt poprzez kopiowanie już istniejącego obiektu, nazywanego prototypem. Ten wzorze jest użyteczny, kiedy potrzebujemy utworzyć nowy obiekt, ale nie chcemy w tym celu używać konstruktora klasy.

Ale dlaczego nie chcielibyśmy używać konstruktora? Jedna z odpowiedzi to kiedy jego użycie jest bardzo pracochłonne i nie chcemy za każdym razem robić tego samego. Może to być pobieranie dużych plików, łączenie z bazą danych i pobieranie danych itp.

Głównym zagrożeniem w używaniu tego wzorca jest potrzeba uważnego zwrócenia uwagi na wybór stylu klonowania. Rozróżniamy dwa rodzaje, płytkie i głębokie kopiowanie (Shallow and Deep Copying).

ShallowVsDeepCopy

Przy kopiowaniu głębokim kopiujemy również obkety, które są wewnątrz naszego kopiowanego obiektu. Przy płytkim kilka obiektów ma referencje do jednego tego samego obiektu. Przy zmianie właściwości jednego z nich, inne również odwołują się do zmienionej wartości.

Przykład problemu

Utworzę klasę obliczającą liczby pierwsze. W celu optymalizacji, w konstruktorze, będziemy obliczać 50 kolejnych liczb pierwszych, tak, żeby nie trzeba było za każdym razem ich obliczać. Liczby pierwsze będą przechowywane w tablicy cache obliczonych liczb pierwszych. Obiekty klasy primeNumber będą reprezentowały jedną n-tą liczbę pierwszą.

Nasz klasa liczb pierwszych działa, podczas tworzenia obiektu podaje, którą z kolei liczbę pierwszą reprezentuje obiekt.

Na razie jest źle, nasz konstruktor wywołany został już dwukrotnie, zajęło mu to po kilka sekund. Teraz spróbujmy skopiować obiekt.

Wygląda dobrze, konstruktor się nie wywołał kolejny raz, wynik poprawny. Dla pewności sprawdźmy wyniki jeszcze raz.

Niestety obiekty primeNumber2 i primeNumbe3 to te same obiekty, nie zostały sklonowane, było to kopiowanie płytkie.

Kopiowanie głębokie (Deep Copying)

W celu przeprowadzenia klonowania zaimplementujemy protokół NSCopying. Definiuje on metodę copyWithZone, w której będziemy określać, jak obiekt ma być klonowany. W celu implementacji protokołu musiałem wcześniej naszą klasę odziedziczyć z klasy NSObject, która dostarcza nam metodę copy() Rozszerzmy naszą klasę PrimeNumber o protokół NSCopying.

W metodzie copyWithZone tworzymy nowy obiekt PrimeNumber. Wykorzystujemy do skopiowania istniejące już parametry, więc nie musimy wywoływać konstruktora z obliczaniem liczb pierwszych.

Podsumowanie

Przedstawiłem tutaj jedno z możliwych rozwiązań problemu. Na pewno nie jest ono jedyna, ani nawet najlepsze, ale obrazuje ogólną ideę zastosowania wzorca prototypu.

Temat kopiowania płytkiego i głębokiego został również bardzo pobieżnie przedstawiony. Nie pisałem nic o kopiowaniu typów referencyjnych, ale jeśli ktoś jest tym tematem zainteresowany, to proszę zostawić informację w komentarzu, rozszerzę temat kopiowania w innym poście.

Michał