2013-01-10 11 views
5

Potrzebuję twojej pomocy. Jestem w trakcie układania skryptu, który może sprawdzić różne warunki, zanim zdolność może zostać wykonana w grze RPG.C#, Jedność - Pojedyncza funkcja przy wielu różnych obiektach

Wszystkie te umiejętności są w poszczególnych klasach (Kula ognia, Leczenie, Trucizna), wszystkie pochodzą z innej abstrakcyjnej klasy (Umiejętność zasięgowa, Zdolność uzdrawiania, Zdolność DOT), które wszystkie są sprzymierzone z klasą abstrakcyjną (Zdolność).

W celu uniknięcia tworzenia wielu funkcji, aby obsłużyć każdą możliwość:

void condition(Fireball f){//test}; 
void condition(Heal f){//test}; 
void condition(Poison f){//test}; 

Próbuję utworzyć pojedynczy wywołanie funkcji, które można podjąć wszystkie rodzaje umiejętności.

void condition(Ability f){//test} 

Do tej pory udało mi się stworzyć obiekt Ognistej Kuli i przekazać go do funkcji.

Fireball _fire = new FireBall(); 
condition(_fire); 

void condition(Ability f){//test} 

Stąd można uzyskać dostęp do wszystkich zmiennych publiczne zainicjowane w klasie zdolności, ale nie mogę uzyskać dostęp do zmiennych publiczne zainicjowane w klasach pochodnych (w zakresie umiejętności, zdolność Healing, umiejętność kropka).

Czy to ja coś zapominam, czy też patrzę na to w niewłaściwej perspektywie? (Nie jestem świetny w używaniu dziedziczenia i zajęć abstrakcyjnych.)

+0

Klasyczny problem polimorfizmu/dziedziczenia OOP :) Trudno jest pomyśleć o rozwiązaniu, które zachowa enkapsulację i zapewni komfort użytkowania. +1. – dreamzor

+0

Co to ma wspólnego z Objective C? –

+0

Przepraszam, domyślam się, że źle to oznaczyłem. –

Odpowiedz

8

Bez znajomości szczegółów funkcji warunku masz dwie opcje.

One można zrobić coś takiego

if (f.GetType() == typeof(FireBall)) 
{ 
    fireBall = (FireBall)f; 
    fireBall.FireTheFireBall(); 
} 
else if (f.GetType() == typeof(Heal)) 
... 

Lub swoją zdolność może mieć abstrakcyjnej metody Activate, których wszystkie klasy pochodne są wymagane do przeciążenia:

class Fireball 
{ 
    public override void Activate() 
    { 
     //do fireball specific things 
     this.FireTheFireBall(); 
    } 

    public void FireTheFireBall() {...}  
} 

class Heal 
{ 
    public override void Activate() 
    { 
     //do healing specific things 
     this.ApplyTheBandage(); 
    } 
    ... 
} 

abstract class Ability 
{ 
    public abstract void Activate(); 
} 

void condition(Ability f){ 
    f.Activate(); //runs the version of Activate of the derived class 
} 

wtedy każda rzecz, która działa ze zdolnością może wywołać someAbility.Activate(), a implementacja zapewniona przez klasę pochodną zostanie wykonana.

Powinieneś również zapoznać się z interfejsami, które przypominają zajęcia abstrakcyjne. Zaletą interfejsów jest możliwość implementacji wielu z nich, podczas gdy ograniczasz się do dziedziczenia tylko z jednej podstawowej klasy abstrakcyjnej. Pomyśl o interfejsie IKnob, który ma funkcje Turn and Pull. Możesz mieć klasę Szuflady, która implementuje IKnob, klasę Drzwi, klasę TrappedDoor, która implementuje Turn i aktywuje pułapkę. Gracz podchodzi do drzwi i uderza przycisk Użyj na nim, i przekazać do otwartego funkcji obiektu, Otwórz (gałka IKnob)

void Open(IKnob knob) 
{ 
    knob.Turn(); 
    knob.Pull(); 
} 

class TrappedDoor:IKnob,IMaterial,ISomethingElse,IHaveTheseOtherCapabilitiesAsWell 
{ 
    private bool TrapAlreadySprung{get;set;} 
    //more complex properties would allow traps to be attached either to the knob, or the door, such that in one case turning the knob activates the trap, and in the other, Pull activates the trap 
    public Turn() { 
    if(! TrapAlreadySprung) 
    { 
     MessageBox("You hit your head, now you're dead"); 
    } 
    } 
} 

Nie ma sposobów, aby sprawdzić, czy coś ma interfejs, więc jeśli jakiś gracz podchodzi do przedmiotu i próbuje z nim porozmawiać, że możesz sprawdzić, czy obiekt ma interfejs ICanTalk, jeśli zadzwoni obiekt.GetReply ("Hello"), a obiekt może odpowiedzieć. Możesz więc rozmawiać o drzwiach i skałach, jeśli sobie tego życzysz. Dostajesz cały kod, który obsługuje rozmowy z rzeczami/wyświetlanie odpowiedzi itp. Współpracując z metodami interfejsu ICanTalk, a następnie inne klasy mogą implementować ICanTalk i każdy z nich decyduje, w jaki sposób reaguje na rozmowę. Ta koncepcja znana jest jako "oddzielenie problemów" i pomaga stworzyć więcej kodu wielokrotnego użytku.

Ważne jest, abyś mógł napisać fragment kodu, algorytm, funkcję itp., Który działa tylko z tym interfejsem, i w ten sposób po uzyskaniu tego kodu działającego z interfejsem, możesz użyć tego interfejsu na dowolna klasa i ta klasa może wykorzystać istniejący kod.

tj. twoja funkcja condition, jeśli zajęło jej interfejs IAbility, po uruchomieniu tego kodu, dowolna utworzona klasa implementująca IAbility może zostać przekazana do funkcji warunku. Funkcja warunku jest odpowiedzialna za robienie tego, co powinna, a klasa implementująca IAbility dba o wszystko, co jest specyficzne dla niej w ramach metod, które wdrożyła.

Oczywiście klasy implementujące klasę abstrakcyjną lub interfejs muszą implementować wymagane metody, więc czasami może się zdarzyć, że duplikujesz kod. Na przykład, jeśli masz podobne klasy, takie jak TrappedDoor i Door, TrappedDoor może zachowywać się jak zwykłe drzwi, jeśli pułapka nie jest ustawiona/już jest sprężysta. Więc możesz albo dziedziczyć z Drzwi w tym przypadku, albo mieć prywatną właściwość Drzwi (znaną jako "kompozycja"). Jeśli pułapka jest już wysklepiona, możesz zadzwonić do klasy Drzwi wejściowe lub Własne drzwi i zadzwonić. Obróć, aby ponownie użyć domyślnego zachowania zwykłych drzwi w przypadku, gdy pułapka nie jest aktywna.

Test if object implements interface

osobiście Najczęściej interfejs i kompozycji, zamiast dziedziczenia. Nie to dziedzictwo jest straszne, ale hierarchie dziedziczenia mogą szybko stać się bardzo skomplikowane.

+0

Dziękuję za odpowiedź. Nie jestem pewien, jak działa druga sugestia, ale uważam, że pierwsza sugestia może zadziałać. Zajrzę do niego tak szybko, jak to będzie możliwe, a ja zgłoś się ponownie :) –

+0

@Marc Zaktualizowano, aby rozwinąć drugi przykład. – AaronLS

+1

Drugi przykład wygląda na o wiele rozsądniejszy pomysł, który podejrzewam ... :) –

Powiązane problemy