2010-10-24 14 views
18

Obecnie pracuję z problemem zależności cyklicznej podczas projektowania moich zajęć.Projekt OO i zależności cykliczne

Odkąd przeczytałem o Anemic Domain Model (coś, co robiłem cały czas), naprawdę próbowałem uciec od tworzenia obiektów domeny, które były po prostu "kubełkami pobierających i ustawiających" i wracają do moich korzeni OO.

Jednak poniżej jest problem, który często spotykam i nie jestem pewien, jak powinienem go rozwiązać.

że mamy zespołu klasy, który ma wiele Gracze. Nie ma znaczenia, jaki to sport :) Zespół może dodawać i usuwać graczy, podobnie jak gracz może opuścić drużynę i dołączyć do innej.

Więc mamy zespół, który ma listę graczy:

public class Team { 

    private List<Player> players; 

    // snip. 

    public void removePlayer(Player player) { 
     players.remove(player); 
     // Do other admin work when a player leaves 
    } 
} 

Następnie mamy gracza, który ma odniesienie do zespołu:

public class Player { 
    private Team team; 

    public void leaveTeam() { 
     team = null; 
     // Do some more player stuff... 
    } 
} 

Można przypuszczać, że zarówno metody (usuwanie i opuszczanie) mają specyficzną dla domeny logikę, która musi być uruchamiana za każdym razem, gdy drużyna usuwa gracza, a gracz opuszcza zespół. W związku z tym, moja pierwsza myśl to, że gdy zespołu wykopuje gracza, removePlayer (...) powinny również wywołać metodę player.leaveTeam() ...

ale co jeśli gracza napędza odjazd - czy metoda leaveTeam() powinna wywoływać team.removePlayer (this)? Nie bez tworzenia nieskończonej pętli!

W przeszłości, właśnie zrobiłbym te obiekty "głupimi" POJO i miał warstwę usług do pracy. Ale nawet teraz mi pozostało z tym problemem: aby uniknąć wzajemnie od siebie zależnych, warstwa usługa nadal ma połączyć to wszystko razem - tzn

public class SomeService { 

    public void leave(Player player, Team team) { 

     team.removePlayer(player); 
     player.leaveTeam(); 

    } 

} 

jestem na komplikowania tego? Być może brakuje mi oczywistej wady konstrukcyjnej. Wszelkie opinie będą mile widziane.


Dziękuję wszystkim za odpowiedzi. Przyjmuję rozwiązanie Grodriguez, ponieważ jest to najbardziej oczywiste (nie mogę uwierzyć, że nie przyszło mi do głowy) i łatwe do wdrożenia. Jednak DecaniBass ma dobry punkt. W opisywanej sytuacji możliwe jest, że gracz opuści zespół (i będzie świadomy, czy jest w zespole, czy nie), a także zespół prowadzący usuwanie. Zgadzam się z twoim punktem widzenia i nie podoba mi się pomysł, że w tym procesie są dwa "punkty wejścia". Dzięki jeszcze raz.

Odpowiedz

14

Możesz przerwać okrągłą zależność, dodając strażników, aby sprawdzić, czy zespół nadal ma gracza/gracza jest jeszcze w drużynie.Na przykład:

W klasie Team:

public void removePlayer(Player player) { 
    if (players.contains(player)) 
    { 
     players.remove(player); 
     player.leaveTeam(); 
     // Do other admin work when a player leaves 
    } 
} 

W klasie Player:

public void leaveTeam() { 
    if (team != null) 
    { 
     team.removePlayer(this); 
     team = null; 
     // Do some more player stuff.. 
    } 
} 
+2

Może być tylko ja, ale lubię go używać, jeśli ... tak oszczędnie jak to możliwe. Zauważyłem, że czyni kod trochę mniej łatwym do utrzymania –

+4

players.remove() zwróci wartość true, jeśli kolekcja została zmieniona; nie trzeba wykonywać .contains(). – KarlP

+0

@KarlP: Wiem, ale myślałem, że wyraźne sprawdzenie poprawi logikę. – Grodriguez

1
public void removePlayer(Player player) { 
    if (players.contains(player)) { 
     players.remove(player); 
     player.leaveTeam(); 
    } 
} 

Ditto wewnątrz leaveTeam.

2

pomysłem jest robić rzeczy związane domenie w różnych metod, które nie nazywają siebie lecz domenę związaną rzeczy dla własnego obiektu, tj. metoda drużynowa działa dla zespołu, a metoda gracza dla gracza:

public class Team { 

    private List<Player> players; 

    public void removePlayer(Player player) { 
     removePlayerFromTeam(player); 
     player.removeFromTeam(); 
    } 
    public void removePlayerFromTeam(Player player) { 
     players.remove(player); 
     //domain stuff 
    } 
} 

public class Player { 
    private Team team; 

    public void removeFromTeam() { 
     team = null; 
     //domain stuff 
    } 
    public void leaveTeam() { 
     team.removePlayerFromTeam(this); 
     removeFromTeam(); 
    } 

} 
+1

Metoda 'leaveTeam()' rzuciłaby NPE, jak wywołujesz 'team.removePlayerFromTeam()' po ustawieniu 'team = null'. – Grodriguez

+0

Również w tym rozwiązaniu wywołanie 'player.leaveTeam()' faktycznie nie usuwa gracza z listy graczy w obiekcie drużyny. Podobnie, wywołanie 'team.removePlayer()' nie ustawi 'team' var na' null' w obiekcie gracza. – Grodriguez

+1

W tym projekcie metody zawierające kod specyficzny dla domeny powinny być pakietami prywatnymi, a nie publicznymi, jak sądzę. Ale to zdecydowanie droga, którą weźmiemy. – Waldheinz

7

Ben,

Zacznę od pytania, czy gracz może (logicznie, prawnie) usunąć siebie z zespołu. Powiedziałbym, że obiekt gracza nie wie, w której drużynie jest (!), Jest on częścią zespołu. Usuń więc Player#leaveTeam() i dokonaj wszystkich zmian w zespole za pomocą metody Team#removePlayer().

W przypadku, gdy masz tylko odtwarzacz i trzeba usunąć go ze swojego zespołu, wówczas można mieć statycznej metody przeglądowej w zespole public static Team findTeam(Player player) ...

wiem, że to jest mniej satysfakcjonująca i naturalne niż metody Player#leaveTeam(), ale z mojego doświadczenia wynika, że ​​nadal możesz mieć sensowny model domeny.

2 sposób odniesienia (Parent -> Dziecko i idealny dla całych> Parent) są często obarczona innymi powiedzieć, zbieranie śmieci, utrzymanie „więzów integralności”, itp

Design to kompromis!

Powiązane problemy