W tej serii postów na blogu Eric Lippert opisuje problem z projektowaniem obiektowym, wykorzystując jako przykład kreatorów i wojowników:
abstract class Weapon { }
sealed class Staff : Weapon { }
sealed class Sword : Weapon { }
abstract class Player
{
public Weapon Weapon { get; set; }
}
sealed class Wizard : Player { }
sealed class Warrior : Player { }
a następnie dodaje kilka zasad:
- Wojownik może używać tylko miecza.
- Czarodziej może używać tylko laski.
Następnie demonstruje problemy, na które napotykasz, gdy próbujesz egzekwować te reguły za pomocą systemu typu C # (np. Czyniąc Wizard
klasę odpowiedzialną za upewnienie się, że czarodziej może korzystać tylko z personelu). Naruszasz Zasadę zastępowania Liskowa, ryzykujesz wyjątki w czasie wykonywania lub kończysz się trudnym do rozszerzenia kodem.
Rozwiązanie, które wymyślił, polega na tym, że klasa Player nie dokonuje weryfikacji. Służy wyłącznie do śledzenia stanu. Następnie zamiast dać graczowi broń:
player.Weapon = new Sword();
stan jest modyfikowany przez Command
s i zgodnie z Rule
s:
... tworzymy
Command
obiekt o nazwie,Wield
który przyjmuje dwa obiekty stanu gry, aPlayer
i aWeapon
. Kiedy użytkownik wydaje polecenie systemowi „ten czarodziej powinien dzierżyć ten miecz”, wówczas polecenie to jest oceniane w kontekście zestawuRule
s, który tworzy sekwencjęEffect
s. Mamy taki,Rule
który mówi, że kiedy gracz próbuje użyć broni, efektem jest to, że istniejąca broń, jeśli taka istnieje, zostaje upuszczona, a nowa broń staje się bronią gracza. Mamy kolejną zasadę, która wzmacnia pierwszą zasadę, która mówi, że efekty pierwszej reguły nie mają zastosowania, gdy czarodziej próbuje użyć miecza.
Zasadniczo podoba mi się ten pomysł, ale mam obawy, jak można go wykorzystać w praktyce.
Wydaje się, że nic nie stoi na przeszkodzie, aby deweloper omijał Commands
and Rule
, po prostu ustawiając Weapon
na Player
. Weapon
Nieruchomość musi być dostępny przez Wield
komendę, więc nie może być wykonana private set
.
Więc, co ma zapobiec deweloper z tej operacji? Czy muszą tylko pamiętać, żeby tego nie robić?