Powiedzmy mam hierarchię Item
klas: Rectangle, Circle, Triangle
. Chcę móc je narysować, więc moją pierwszą możliwością jest dodanie wirtualnej Draw()
metody do każdej z nich:
class Item {
public:
virtual ~Item();
virtual void Draw() =0;
};
Chcę jednak podzielić funkcję rysowania na osobną bibliotekę Draw, podczas gdy biblioteka Core zawiera tylko podstawowe reprezentacje. Jest kilka możliwości, o których mogę myśleć:
1 - A, DrawManager
który bierze listę Item
si i musi użyć, dynamic_cast<>
aby dowiedzieć się, co zrobić:
class DrawManager {
void draw(ItemList& items) {
FOREACH(Item* item, items) {
if (dynamic_cast<Rectangle*>(item)) {
drawRectangle();
} else if (dynamic_cast<Circle*>(item)) {
drawCircle();
} ....
}
}
};
Nie jest to idealne, ponieważ opiera się na RTTI i zmusza jedną klasę do bycia świadomym wszystkich pozycji w hierarchii.
2 - Drugim podejściem jest odłożenie odpowiedzialności na ItemDrawer
hierarchię ( RectangleDrawer
itp.):
class Item {
virtual Drawer* GetDrawer() =0;
}
class Rectangle : public Item {
public:
virtual Drawer* GetDrawer() {return new RectangleDrawer(this); }
}
Uzyskuje się to rozdzielenie obaw między podstawową reprezentacją przedmiotów a kodem do rysowania. Problem polega jednak na tym, że klasy przedmiotów są zależne od klas rysunku.
Jak mogę oddzielić ten kod rysunkowy do osobnej biblioteki? Czy rozwiązaniem jest zwrócenie Przedmiotów do klasy fabrycznej określonego opisu? Jak można to jednak zdefiniować, aby biblioteka Core nie była zależna od biblioteki Draw?
źródło
visit()
metodę.Podział, który opisujesz - na dwie równoległe hierarchie - jest zasadniczo wzorem mostu .
Martwisz się, że uzależnia to wyrafinowaną abstrakcję (Prostokąt itp.) Od implementacji (RectangleDrawer), ale nie jestem pewien, jak możesz tego całkowicie uniknąć.
Z pewnością możesz dostarczyć abstrakcyjną fabrykę, aby przełamać tę zależność, ale nadal potrzebujesz sposobu, aby powiedzieć fabryce, którą podklasę utworzyć, a ta podklasa nadal zależy od konkretnej dopracowanej implementacji. Oznacza to, że nawet jeśli Rectangle nie zależy od RectangleDrawer, nadal potrzebuje sposobu, aby poinformować fabrykę o jego zbudowaniu, a sam RectangleDrawer nadal zależy od Rectangle.
Warto również zapytać, czy jest to przydatna abstrakcja. Czy rysowanie prostokąta jest tak trudne, że warto umieścić go w innej klasie, czy naprawdę próbujesz wyodrębnić bibliotekę graficzną?
źródło
RectangleDrawer
,CircleDrawer
itd. Nie może być najbardziej przydatny sposób abstrakcyjny biblioteki graficznej. Jeśli zamiast tego odsłonisz zestaw prymitywów za pośrednictwem zwykłego abstrakcyjnego interfejsu, nadal przełamujesz zależność.Zobacz Semantykę Wartości i polimorfizm oparty na pojęciach .
Ta praca zmieniła moje spojrzenie na polimorfizm. Korzystaliśmy z tego systemu, aby mieć duży wpływ na powtarzające się sytuacje.
źródło
Powodem, dla którego masz problem, jest to, że obiekty nie powinny się rysować. Prawidłowe narysowanie czegoś zależy od miliarda kawałków stanu, a prostokąt nie będzie miał żadnego z tych kawałków. Powinieneś mieć centralny obiekt graficzny, który reprezentuje stan rdzenia, taki jak backbuffer / bufor głębokości / shadery / etc, a następnie dać mu metodę Draw ().
źródło
Dog
lubCat
- obiekt odpowiedzialny za ich narysowanie jest o wiele bardziej złożony niż za narysowanie prostokąta.