Nazwa „prototyp” może brzmieć znajomo dla programistów JavaScriptu. Nic dziwnego, w końcu to mechanizm, na którym opiera się cała struktura obiektowości języka. Ale prototypy nie muszą służyć tylko do wprowadzania do kodu dziedziczenia. Czasem oprócz tego, potrzebna jest też możliwość kopiowania, czy też „klonowania” obiektów.
Dziś przedstawię swoją wariację na temat klasycznego wzorca Prototypu. Implementacja ta będzie znacznie uproszczona względem oryginału, ale warto się z nią zapoznać, aby zrozumieć ogólną ideę wzorca prototypu. Kto wie, może akurat przyda się w waszych JSowych programach.
Czym więc jest wzorzec prototypu? Jest to mechanizm, służący do tworzenia nowych instancji obiektów, poprzez klonowanie. Bardzo często, we wzorcu znajduje się obiekt nazywany fabryką prototypów. Obiekt ten posiada mechanizmy, które pozwalają na stworzenie kopii dowolnego innego obiektu. Podkreślam, chodzi o kopie, nie o referencję.
Rozwiązanie to pozwala na szybkie tworzenie nowych obiektów w czasie działania programu a nie podczas jego kompilacji. Chociaż to w JavaScripcie to akurat nie ma takiego znaczenia 🙂 . Ponad to tworzenie odbywa się bez udziału konstruktora co w niektórych językach jest też bardziej wydajne.
Do czego może się przydać taki wzorzec? Dobrym scenariuszem, w którym można byłoby wykorzystać ten wzorzec, jest potrzeba zapisywania stanu jakiegoś obiektu. Za każdym razem gdy stan obiektu się zmienia, można sklonować jego pierwotny stan i w ten sposób przechowywać historię stanów. Dzięki temu można później łatwo wrócić do wcześniejszego stanu.
Zaimplementowanie takiego rozwiązania w JavaScripcie, jest bardzo łatwe. Może zaskoczy was jego prostota, ale to naprawdę wszystko czego potrzeba aby wprowadzić do programu wzorzec prototypu:
1 2 3 4 5 6 7 8 9 10 11 |
var CloneFactory = { clone: function(original) { var clone = original.constructor(); for (var attr in original) { if (original.hasOwnProperty(attr)) { clone[attr] = original[attr]; } } return clone; } } |
Prawda, że nic skomplikowanego? CloneFactory zawiera jedną metodę: clone. Clone jako argument przyjmuje dowolny inny obiekt. Wewnątrz metody tworzony jest kolejny, pusty obiekt. Następnie, otrzymuje on takie pola i metody takie jak ten przekazany w argumencie. Do tego, nowy obiekt ma te same wartości co argument. Na końcu metoda clone, zwraca stworzony obiekt. I to wszystko.
W JavaScripcie, gdzie tworzenie i zmienianie obiektów w trakcie działania programu jest bardzo proste, ten wzorzec może nie wydaje się być specjalnie potężny. Prawda jest jednak taka, że spokojnie można znaleźć dla niego kilka zastosowań, chociażby zapisywanie stanu innych obiektów, o którym pisałem wyżej. Oto przykład:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
var CloneFactory = { clone: function(original) { var clone = original.constructor(); for (var attr in original) { if (original.hasOwnProperty(attr)) { clone[attr] = original[attr]; } } return clone; } } var obj = { field: 0, incrField: function(){ this.field++; } } console.log("original object field value: ", obj.field) obj.incrField(); console.log("original object field value after increase: ", obj.field) obj2 = CloneFactory.clone(obj); console.log("clone object field value: ", obj2.field) obj.incrField(); console.log("original object field value after second increase: ", obj.field) console.log("clone object field value second increase: ", obj2.field) |
Dzięki PrototypeFactory, mogę bez problemu stworzyć kopię obiektu obj, niezależnie od tego jak rozbudowany by był. Nawet gdy po fakcie, struktura obiektu obj, zmieni się, mój klon będzie pamiętał wszystkie jego dawne właściwości.
Ważnym elementem tego schematu jest to, że PrototypeFactory nie zdaje sobie sprawy z istnienia innych obiektów, jest od nich kompletnie niezależna. Mamy więc bardzo fajne rozdzielenie obowiązków, a fabryka klonów może przydać się w wielu różnych sytuacjach.
I to wszystko na dziś. Ten wzorzec jest bardzo prosty, ale kolejne mogą być już bardziej skomlpikowane. Jeżeli chcesz być na bieżąco z wpisami na blogu zachęcam do polubienia mojej strony na facebooku. Zawsze zamieszczam tam informacje o wszystkich nowościach. Jest to też dobre miejsce na kontakt ze mną. Na wszystkie pytania zawsze odpowiem :).
EDIT 14/09/2016:
Do oryginalnej wersji posta wkradł się mały błąd. Metoda kopiująca obiekty, nie do końca spełniała swoje zadanie. Na szczęście zwrócono mi uwagę na FB i mogłem poprawić babola. Dzięki wszystkim czytelnikom za czujność! Teraz w necie nie będzie trefnego artykułu, a ja nauczyłem się czegoś nowego. Win-Win 🙂