Projektuję swój mały program OOP do symulacji wampirów, wilków, ludzi i ciężarówek i staram się wdrożyć moje własne ograniczone rozumienie interfejsów.
( Nadal tu streszczam i nie mam jeszcze implementacji kodu, więc jest to raczej kwestia projektu OOP ... myślę!)
Czy mam rację, szukając „wspólnego zachowania” między tymi klasami i wdrażając je jako interfejsy ?
Na przykład gryzą wampiry i wilki ... więc powinienem mieć interfejs gryza?
public class Vampire : Villain, IBite, IMove, IAttack
Podobnie w przypadku samochodów ciężarowych ...
public class Truck : Vehicle, IMove
A dla ludzi ...
public class Man : Human, IMove, IDead
Czy moje myślenie jest tutaj? (Doceniam Twoją pomoc)
object-oriented
interfaces
użytkownik3396486
źródło
źródło
IEnumerable
,IEquatable
itpOdpowiedzi:
Ogólnie rzecz biorąc, chcesz mieć interfejsy dla wspólnych cech swoich klanów.
Częściowo zgadzam się z @Robert Harvey w komentarzach, który powiedział, że zwykle interfejsy reprezentują bardziej abstrakcyjne cechy klas. Niemniej jednak uważam, że począwszy od bardziej konkretnych przykładów dobry sposób na rozpoczęcie myślenia abstrakcyjnego.
Chociaż twój przykład jest technicznie poprawny (tzn. Tak, zarówno wampiry, jak i wilki gryzą, więc możesz mieć do tego interfejs), istnieje jednak kwestia istotności. Każdy obiekt ma tysiące cech (np. Zwierzęta mogą mieć futro, mogą pływać, wspinać się na drzewa itd.). Czy stworzysz interfejs dla nich wszystkich? Bardzo mało prawdopodobne.
Zwykle chcesz, aby interfejsy dla rzeczy, które mają sens, były grupowane w aplikacji jako całości. Na przykład, jeśli budujesz grę, możesz mieć szereg obiektów IMove i zaktualizować ich pozycję. Jeśli nie chcesz tego robić, interfejs IMove jest całkiem bezużyteczny.
Chodzi o to, żeby nie przesadzać z inżynierem. Musisz pomyśleć o tym, jak zamierzasz korzystać z tego interfejsu, a dwie klasy posiadające wspólną metodę nie są wystarczającym powodem do utworzenia interfejsu.
źródło
IBite
nie jest szczególnie przydatny, ale możesz chciećIAttack
, abyś mógł pracować nad wszystkimi rzeczami, które robią ataki, lubIUpdate
abyś mógł uruchamiać aktualizacje dla wszystkiego lubIPhysicsEnabled
abyś mógł zastosować do nich fizykę itp.Wygląda na to, że tworzysz kilka interfejsów z pojedynczą metodą . Na pierwszy rzut oka jest to w porządku, ale należy pamiętać, że interfejsy nie są własnością klas, które je implementują. Są własnością klientów, którzy ich używają. Klienci decydują, czy coś musi być czymś, co może się poruszać i atakować.
Jeśli mam
Combat
klasę zfight()
metodą, która metoda prawdopodobnie ma potrzeby, aby zadzwonić zarównomove()
iattack()
na tym samym obiekcie. To zdecydowanie sugeruje potrzebęICombatant
interfejsu, któryfight()
można wywoływaćmove()
iattack()
przesyłać. Jest to czystsze niżfight()
wzięcieIAttack
obiektu i rzucenie go,IMove
aby sprawdzić, czy może się również poruszać.To nie znaczy, że nie możesz mieć także
IMove
IAttack
interfejsów. Mam tylko nadzieję, że nie robisz ich bez jakiegoś klienta, który ich potrzebuje. I odwrotnie, jeśli żaden klient nigdy nie musi zmuszać obiektu do ruchu i ataku,ICombatant
nie jest to konieczne.Ten prosty sposób patrzenia na interfejsy jest często gubiony, ponieważ ludzie lubią następujące przykłady. Pierwsze interfejsy, na które jesteśmy narażeni, znajdują się w bibliotekach. Niestety biblioteki nie mają pojęcia, czym są ich klienci. Mogą więc zgadywać tylko potrzeby swoich klientów. Nie najlepszy przykład do naśladowania.
źródło
Zastanów się, czy powszechne będzie posiadanie kolekcji obiektów o różnych kombinacjach umiejętności i czy kod może chcieć wykonać akcję na tych elementach w obrębie kolekcji, które je obsługują . Jeśli tak, i jeśli byłoby rozsądne „domyślne zachowanie” dla obiektów, które nie mają użytecznego wsparcia dla niektórych działań, pomocne może być wdrożenie interfejsów przez szeroką gamę klas, a nie tylko tych, które mogą zachowywać się pożytecznie.
Załóżmy na przykład, że tylko kilka rodzajów stworów może mieć Woozle i ktoś chce, aby takie stworzenia miały
NumerOfWoozles
własność. Gdyby taka właściwość znajdowała się w interfejsie, który został zaimplementowany tylko przez stworzenia, które mogą mieć Woozle, wówczas kod, który chciał znaleźć całkowitą liczbę Woozles przechowywanych przez zbiór stworzeń mieszanych typów, musiałby powiedzieć coś w stylu:Gdyby jednak WoozleCount było członkiem Creature / ICreature, chociaż kilka podtypów zastąpiłoby domyślną implementację WoozleCount Creature, która zawsze zwraca zero, kod można uprościć w celu:
Chociaż niektórzy ludzie mogą się obawiać, że każde stworzenie stworzy właściwość WoozleCount, która jest naprawdę użyteczna tylko dla kilku podtypów, właściwość ta byłaby znacząca dla wszystkich typów, niezależnie od tego, czy przydałaby się w przypadku elementów tego typu, i uważałbym, że interfejs „zlewu kuchennego” jest mniej zapachowy niż operator Trycast.
źródło