W poprzednim artykule wróciłem do przeszłości odkrywając swój dawny framework. Dziś chciałbym ponownie tego dokonać i przedstawić kolejny element frameworka, mianowice encje. Zapraszam!
Architektura komponentowa
Intense podobnie jak framework CraftyJS, realizuje metodykę w oparciu o komponenty. CraftyJS nie posiada jednak systemów, cała logika zawarta jest w komponentach. Dodatkowo sam mechanizm wiązania encji z komponentami jest rozwiązany w CraftyJS za pomocą „wstrzykiwania” komponentów do encji. Takie zachowanie może doprowadzić do powstania bardzo dużych obiektów w przypadku gdy encja posiada wiele komponentów. Ponadto tworzenie zbiorów encji o takich samych komponentach jest niewydajne, ponieważ metody komponentów nie są w żaden sposób współdzielone. W intense encje są jedynie prostymi obiektami, które posiadają ukryte pole stanowiące identyfikator encji. Komponenty przechowują jedynie dane, aczkolwiek framework umożliwia również wydajne dodawanie metod bezpośrednio do komponentów. Systemy dostarczają niezbędną logikę i operują jedynie na tych encjach, które mają określony przez dany system zbiór komponentów.
Encje
Encje we frameworku intense są instancjami konstruktora entity, który jest hermetyzowany w otoczeniu managera encji. Utworzenie najprostszej encji polega na wywołaniu następującego kodu:
intense.entity();
Zostanie zwrócona w ten sposób encja posiadająca tylko jedno pole, będące nieujemnym identyfikatorem numerycznym. pole identyfikatora jest publiczne aby ułatwić serializację obiektu i w przyszłości być wykorzystane do szybkiego klonowania encji poprzez zwykłą podmianę identyfikatora. Intense umożliwia także tworzenie encji posiadających nazwy, dla bardziej czytelnego rozróżnienia a także dane, jeżeli programista planuje stworzyć encję o unikalnych właściwościach, które nie powinny być dostarczone do innych jednostek w postaci komponentów. Przykładowe utworzenie encji z nazwą i danymi wygląda następująco:
intense.entity({ name: "Encja 1", data: { x: 0, y: 0 } });
Wiązanie encji z komponentami polega na utworzeniu dla każdego komponentu słownika, którego kluczami są identyfikatory encji, natomiast wartościami obiekty komponentów. Wiązania są przechowywane w managerze encji w prywatnej zmiennej componentsBinding
i operacje na tym zbiorze są dostępne jedynie poprzez publiczne metody managera. Aby powiązać komponent z encją, należy wykorzystać jedną ze składni:
var encja = intense.entity({ components: ["Testowy_komponent", "Testowy_komponent2"] }); encja.add("Testowy_komponent3");
Wykorzystanie metody add ma większe możliwości, ponieważ można w ten sposób dodawać komponenty nie tylko za pomocą nazwy, ale także utworzonych wcześniej obiektów komponentów. Aby usunąć komponent z encji należy wywołać metodę remove z nazwą komponentu. Encje można również tworzyć na podstawie innych encji, co zostało przedstawione na poniższym listingu:
var encja = intense.entity(); var encja2 = intense.entity(encja); var encja3 = intense.entity({ name: "specjalna", entity: encja2 });
Utworzone w ten sposób encje mogą posiadać odziedziczoną nazwę, dane, jak i również komponenty encji rodzica. Oprócz możliwości tworzenia encji, konstruktor może wyszukiwać encję jeżeli w parametrze otrzyma nazwę encji lub identyfikator. Dzięki prototypowej naturze języka JavaScript, metody wspomagające pracę z encjami nie są umieszczane bezpośrednio w konstruktorze, lecz w prototypie encji. Takie rozwiązanie powoduje, że encje spełniają założenia architektury a potrzebne metody są współdzielone poprzez wszystkie instancje konstruktora i nie są w żaden sposób powielane. Lista pozostałych metod konstruktora prezentuje się następująco:
- toggle – usuwa jeden komponent a w jego miejsce dodaje inny.
- removeAll – usuwa z encji wszystkie komponenty
- has – sprawdza czy encja posiada dany komponent.
- set – nadpisuje wybrany komponent nowym obiektem.
- get – zwraca obiekt komponentu o podanej nazwie.
- getAll – zwraca słownik komponentów encji.
- getListOfComponents – zwraca listę komponentów encji.
- destroy – usuwa encję i powiązane z nią komponenty.
- getName – zwraca nazwę encji.
- toString – zwraca szczegóły encji w postaci łańcucha znaków.
Metody encji są odpowiednikami metod zawartych w managerze encji. Manager encji posiada możliwości manipulowania encjami, tworzenia ich zbiorów, sprawdzania czy dany obiekt jest encją i zarządzania powiązaniami z komponentami. Oprócz tego, posiada mechanizm generowania unikalnych identyfikatorów dla encji.
Zbiór encji
Do zarządzania dużymi zbiorami jednostek, framework intense wykorzystuje obiekt zbioru encji EntitySet. EntitySet łączy w zbiór encje o podanym w parametrze komponencie lub komponentach. Jeżeli nie zostanie podany żaden parametr to EntitySet utworzy zbiór encji dla wszystkich komponentów. Przykładowe użycie zbioru:
var encje = intense.EntitySet("Testowy_komponent");
EntitySet komunikuje się z managerem zdarzeń poprzez metodę listen
. Wywołując tą metodę, EntitySet zarejestruje w obiekcie managera swoje metody manipulujące encjami, które zostaną wywołane gdy we frameworku zajdzie określone zdarzenie. Jednym z takich zdarzeń jest newEntity
, uruchamiane w momencie utworzenia nowej encji. EntitySet nasłuchując tego zdarzenia, sprawdzi nowo dodaną encję i jeżeli spełnione są wymogi co do komponentów to encja zostanie umieszczona w zbiorze. Dzięki temu mechanizmowi programista nie musi się martwić o aktualizację zbioru, gdy w innej części programu zostaną zmodyfikowane encje będące częścią tego zbioru. Najważniejsze metody zbioru EntitySet:
- add – dodaje encje do zbioru jeżeli spełniony jest warunek co do wymaganych komponentów.
- clear – usuwa wszystkie encje ze zbioru.
- exclude – wyklucza encje o danym komponencie ze zbioru.
- include – dodaje wymagane komponenty do zbioru.
- remove – usuwa encję z kolekcji.
- removeByComponent – usuwa encję na podstawie komponentu.
- hasComponent – sprawdza czy zbiór posiada dany komponent.
- contains – sprawdza czy kolekcja posiada daną encję.
- getArray – zwraca kolekcję encji jako tablicę.
- size – zwraca ilość elementów kolekcji.
- get – zwraca podaną encję z kolekcji.
- getByComponent – zwraca encję na podstawie komponentu.
- each – wykonuje iterację na kolekcji.
- listen – rejestruje część metod na nasłuchiwanie zdarzeń.
- removeListeners – usuwa nasłuchiwanie zdarzeń.
EntitySet jest częścią każdego systemu. To rozwiązanie ułatwia aktualizację informacji pomiędzy systemami jeżeli modyfikują one stan tych samych encji.
Co dalej?
Czytając swoje stare zapiski sam się zaskoczyłem ilością informacji jakie kiedyś zebrałem i dlatego postanowiłem podzielić omawianie intense.js na kilka artykułów. W dalszej kolejności będzie opis komponentów.