Klasy parametryzowane wytycznymi
Z Wikipedii
Klasy parametryzowane wytycznymi pierwszy wynalazł i omówił Andrei Alexandrescu w [1]. Wytyczne są techniką metaprogramowania w C++ z wykorzystaniem szablonów, umożliwiającą uzyskanie dużo większej elastyczności w projektowaniu klas.
Konstruowanie klas parametryzowanych wytycznymi polega na składaniu klasy z wykorzystaniem innych klas (tzw. wytycznych), z których każda określa pewien fragment zachowania głównej klasy. W praktyce, można myśleć o klasach wytycznych jako o pewnym rodzaju pluginów, które doczepiane do klas, umożliwiają elastyczne, bezpieczne typologicznie i efektywne dostosowywanie klas przez użytkownika, bez ręcznego pisania setek różnych wariantów, które różnią się tylko szczegółami działania. Jest to technika bardzo podobna do wzorca projektowego Strategia, tyle tylko że tam zmiana działania obiektów może następować dynamiczne, podczas działania programu, natomiast wytyczne ustalają zachowanie już podczas kompilacji.
[edytuj] Przykład
Załóżmy że piszemy klasę inteligentnego wskaźnika i, ze względów efektywnościowych, chcemy dać użytkownikowi możliwość określenia czy danym przypadku ma zachodzić przeprowadane testowanie poprawności, czy nie. Jednym z możliwych rozwiązań tego problemu jak dodanie parametru do konstruktora obiektu, który determinuje jak inteligentny wskaźnik ma się zachowywać. Jednak wspomniane względy efektywnościowe przestrzegają nas przed tym, gdyż należałoby przeprowadzać testowanie if w każdym miejscu kodu, co wprowadzałoby dodatkowy narzut nawet wtedy, gdy użytkownik wybrałby wariant bez testowania poprawności.
Standardowa klasa SmartPtr może zostać skrótowo zapisana w ten sposób:
template <class T> class SmartPtr { // ... };
Aby wykorzystać wytyczne, do klasy SmartPtr musimy dodać jeszcze jeden parametr szablonu, który będzie ustalał sposób obsługi błędów. Tak więc, od tej pory nasza klasa SmartPtr może wyglądać np. tak:
template <class T, class ErrorHandling> class SmartPtr : public ErrorHandling { // ... };
Dziedziczymy publicznie od klasy ErrorHandling, tak więc wszystkie metody tej klasy od tej pory wchodzą w skład klasy SmartPtr. Co więcej, zachowanie tych metod zmienia się w zależności od tego jaką klasę wyślemy w miejsce ErrorHandling. Tak więc, gdy wywołamy np. metodę void ErrorHandling :: Check(), to gdy ErrorHandling będzie równe NoChecking, wtedy nie będzie żadnego sprawdzania. Natomiast gdy wyślemy StrictCheckcing, to każda dereferencja wskaźnika przy wywołaniu (w tym przypadku) metody Get() będzie sprawdzana:
template <class T> struct NoChecking { void Check(T * _ptr) { /* pusto, brak sprawdzania poprawności */ } } template <class T> struct StrictChecking { void Check(T * _ptr) { if (0 == ptr) { /* obsłuż błąd */ } } } // Opis klasy: // Wytyczna ErrorHandling musi posiadać metodę void Check(T*) template <class T, class ErrorHandling> class SmartPtr : public ErrorHandling { public: T * Get() const { Check(ptr); // <----- !!! zmienia się w zależności od ErrorHandling return ptr; } private: T * ptr; }; // Używanie: struct Foo { void Bar() { } }; SmartPtr<Foo, NoChecking<Foo> > ptrNoCheck = new Foo(); ptrNoCheck.Get()->Bar(); // Get() nie sprawdzane SmartPtr<Foo, StrictChecking<Foo> > ptrChecked = new Foo(); ptrChecked.Get()->Bar(); // Get() sprawdzane
Aby dodać wsparcie dla np. wielowątkowości i liczenia referencji, i jednocześnie pozostawić użytkownikowi możliwość wyboru zachowania klasy, jak również dodawania własnych "strategii zachowań", wystarczy dodać kolejne wytyczne. W przypadku stosowania innych technik, osiągnięcie podobnych rezultatów najprawdopodobniej nie byłoby tak łatwe.
Wytyczne są techniką użyteczną szczególnie dla twórców bibliotek i silników, ponieważ umożliwiają pisanie kodu, który może być w łatwy, przenośny, efektywny i bezpieczny typologicznie sposób rozszerzany przez użytkowników.
[edytuj] Linki zewnętrzne
[1] - Andrei Alexandrescu, Nowoczesne projektowanie w C++, WNT, 2005
Przykładowy, pierwszy rozdział z powyższej pozycji, zawierający opis wytycznych (po angielsku)
Zastosowanie wytycznych w grach komputerowych do stworzenia elastycznego systemu cząsteczkowego