2012-09-19 17 views
9

Jestem programistą (gra jako hobby) i widziałem, jak korzystam z następującego paradygmatu kilka razy. (Zarówno w tworzeniu architektury serwera, jak i przy tworzeniu gier wideo.) Wydaje się to naprawdę brzydkie, ale nie wiem, jak się obejść. Podam przykład w grze, ponieważ ostatnio to zauważyłem. To jest gra RPG, nad którą pracuję. Za każdym razem, gdy rozpoczyna się bitwa, CombatEngine tworzy dwie partie Combatants. Każdy Combatant konfiguruje obiekt ArtificialIntelligence, który jest skojarzony z danym bojownik, który jest odpowiedzialny za dyktując ruchów dla graczy, które nie otrzymują wyraźne polecenie:Weird reference passing in class construction

public class Combatant { 

    ArtificialIntelligence ai = null; 

    public Combatant() 
    { 
     // Set other fields here. 

     this.ai = new ArtificialIntelligence(this); 
    } 

} 

Oto co mi się nie podoba: pole wewnętrzne (ArtificialIntelligence) bierze Combatant podczas budowy, ponieważ potrzebuje niektórych pól Combatant, aby dyktować odpowiednie działania. Tak więc, dla wygody, utrzymuję odniesienie do kombatanta przekazanego jako argument do obiektu Sztuczna Inteligencja, ale ten obiekt zawiera odniesienie do samego obiektu ai! Tworzy to dziwną rekurencję, ale nie wiem, jak to obejść. Obiekt sztucznej inteligencji potrzebuje dużej ilości pól, które są specyficzne dla walczącego, dlatego właśnie przeszedłem przez cały obiekt, ale nie podoba mi się, jak obiekt zawiera odniesienie do pola ai, które jest zawarte w overying fightant pole, które znajduje się w overliście klasy ai. Czy to zła praktyka, czy po prostu nad tym się zastanawiam?

+6

Czy otrzymujesz błąd stackoverflow? Wątpię w to, a jeśli nie, to nie ma tu żadnej rekurencji, tylko przekazanie referencji. Myślę, że masz tutaj problem. –

+1

Woops, z zawodu jestem matematykiem, więc brakuje mi niektórych terminów. Masz rację, to tylko referencja. Czy nadal będzie to uważane za problem? Czy nie jest złą praktyką zagnieżdżanie odniesień w tym formularzu? I żeby odpowiedzieć na twoje pytanie, nie dostaję żadnych błędów. Po prostu pomyślałem, że to źle wygląda i chciałem zdobyć opinie. – Sal

Odpowiedz

9

Chociaż nie ma „projekt” problem tutaj - to tylko odniesienie jesteś przejazdem - jednym ważnym czynnikiem jest to, że należy zainicjować wszystkich dziedzinach przed do przechodzenia this do drugiej klasy. W przeciwnym razie druga klasa będzie miała dostęp do this w potencjalnie niespójnym stanie. Jest to czasami nazywane przepuszczaniem "this" od konstruktora.

Nie rób tego ...

public class BadCombatant { 

    ArtificialIntelligence ai = null; 
    String someField; 

    public BadCombatant() { 
     this.ai = new ArtificialIntelligence(this); 
     // Don't do this - ArtificialIntelligence constructor saw someField as null 
     someField = "something"; 
    } 
+1

+1 Niektóre IDE ostrzegą przed "wyciekiem" w konstruktorze. –

+1

Jasne jest, że 'Konstruktor' ArtificialIntelligence' (i wszelkie metody, które wywołuje) zobaczą 'someField' jako' null'. –

+1

Nie sądzę, że dobrym pomysłem jest utrzymywanie tej cyklicznej zależności. Zobacz moją odpowiedź! Nie ma potrzeby, aby te dwie klasy częściowo zależały od siebie, gdy zależność ta może zostać przeniesiona do jednej z dwóch klas lub zupełnie nowej klasy. – CKing

4

pewno uniknąć zależność cykliczną. Wchodzi jedna zasada odpowiedzialności na ratunek. Możesz wyeliminować potrzebę odwoływania się do sztucznej inteligencji w Combatant, pozwalając ArtificialIntelligence działać na Combatant. Przenieś cały kod z Combatant, który zależy od ArtificialIntelligence do ArtificialIntelligence. CombatEngine wykona następujące czynności:

  1. Utwórz niezależną instancję Combatant, która nie ma nic wspólnego z Artificialntelligence.

  2. Utwórz odpowiednią instancję Sztucznej Inteligencji i przekaż ją wcześniej utworzonemu Kombatantowi.

Alternatywnie można utworzyć nową klasę o nazwie CombatController, która jest przekazywana jako Combatant i ArtificialIntelligence. CombatEngine zrobi następujące:

  1. Tworzenie bojownik bez zależności na inne klasy

  2. Załóż Artificialntelligence bez uzależnienia od jakiejkolwiek innej klasy

  3. Tworzenie CombatController i przekazać go obiekty Combatant i ArtificialIntelligence do użycia. CombatController powinien udostępniać metody kontrolowania Combatant oraz obsługi zachowania AI.

Bez względu na to, z której metody skorzystasz, eliminujesz cykliczną zależność, która Cię niepokoi.

Przykro mi, że nie mogę podać przykładu kodu, ponieważ wpisuję tę odpowiedź z mojego telefonu komórkowego i formatowanie jest uciążliwe.

+0

+1 Są oczywiście jeszcze więcej opcji; można na przykład wyodrębnić pole z Combatant, którego ArtificialIntelligence potrzebuje do nowej klasy, na przykład CombatantState. CombatantState będzie polem w Combatant, a ArtificialIntelligence może zaakceptować CombatantState. Najlepsze rozwiązanie zależy od potrzeby twojego kodu. – sleske

+0

Całkowicie się zgadzam. Jakie podejście do użytkowania może być jasne tylko wtedy, gdy masz cały obraz, a nie tylko część problemu. Tak czy inaczej, cykliczna zależność może być łatwo uniknięta! – CKing