2010-10-24 8 views
18

В настоящее время я борюсь с проблемой круговой зависимости при разработке моих классов.OO дизайн и круговые зависимости

С тех пор как я читал о Anemic Domain Model (что-то, что я делал все время), я действительно пытался уйти от создания объектов домена, которые были всего лишь «ведрами геттеров и сеттеров» и возвращаться к моим корням OO.

Однако проблема ниже такова, что я сталкиваюсь со многими, и я не уверен, как я должен ее решить.

Скажите, что у нас есть Команда класс, который имеет много Игроки. Не имеет значения, какой спорт это :) Команда может добавлять и удалять игроков, так же, как игрок может покинуть команду и присоединиться к другому.

Таким образом, мы имеем команду, которая имеет список игроков:

public class Team { 

    private List<Player> players; 

    // snip. 

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

Тогда у нас есть плеер, который имеет ссылку на команды:

public class Player { 
    private Team team; 

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

Можно предположить, что оба методы (удалить и оставить) имеют специфичную для домена логику, которая должна выполняться всякий раз, когда команда удаляет игрока, а игрок покидает команду. Поэтому моя первая мысль, что когда команды ногами игрока, removePlayer (...) должен также вызвать метод player.leaveTeam() ...

Но что если игрока является движущей отъезд - должен ли метод leaveTeam() вызвать команду.removePlayer (это)? Не без создания бесконечного цикла!

В прошлом, я бы просто сделал эти объекты «немыми» POJO и имел сервисный уровень. Но даже сейчас я все еще остаюсь с этой проблемой: чтобы избежать циклических зависимостей, уровня обслуживания до сих пор связывает все это вместе - то есть

public class SomeService { 

    public void leave(Player player, Team team) { 

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

    } 

} 

ли я над усложняя это? Возможно, мне не хватает очевидного недостатка в дизайне. Любая обратная связь будет принята с благодарностью.


Спасибо всем за ответы. Я принимаю решение Grodriguez, поскольку оно является наиболее очевидным (не могу поверить, что это не произошло со мной) и легко реализуется. Однако DecaniBass действительно подходит. В ситуации, о которой я рассказывал, игрок может покинуть команду (и быть в курсе, находится ли он в команде или нет), а также команда, ведущая удаление. Но я согласен с вашей точкой зрения, и мне не нравится идея о том, что в этот процесс есть две «точки входа». Еще раз спасибо.

ответ

14

Вы можете сломать круговую зависимость, добавив охранников, чтобы проверить, есть ли у команды игрок/игрок по-прежнему находится в команде.Например:

В классе Team:

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

В классе Player:

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

Может быть, это только я, но мне нравится использовать, если ... еще так же экономно, насколько это возможно. Я заметил, что код делает немного менее поддерживаемым –

+4

players.remove() вернет true, если коллекция была изменена; не нужно делать .contains(). – KarlP

+0

@KarlP: Я знаю, но я думал, что явная проверка сделает логику более ясной. – Grodriguez

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

Ditto внутри leaveTeam.

2

Идея заключается в том, чтобы сделать домен связанные вещи в различных методах, которые не называют друг друга, но делает домен связанный материал для их собственного объекта, то есть метод команды делает это для команды и игрока, делает это для игрока

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

Метод 'leaveTeam()' выбрал бы NPE по вашему вызову 'team.removePlayerFromTeam()' после установки 'team = null'. – Grodriguez

+0

Также в этом решении вызов 'player.leaveTeam()' фактически не удаляет игрока из списка игроков в командном объекте. Аналогично, при вызове 'team.removePlayer()' не будет задано 'team' var' to null в объекте игрока. – Grodriguez

+1

В этом дизайне методы, содержащие код, специфичный для домена, должны быть приватными пакетами, а не публичными. Но это определенно маршрут, который я возьму. – Waldheinz

7

Ben,

Я бы хотел спросить, может ли игрок (логически, юридически) убрать себя из команды. Я бы сказал, что объект игрока не знает, в какой команде он (!), Он является частью команды. Итак, удалите Player#leaveTeam() и выполните все изменения команды через метод Team#removePlayer().

В том случае, если у вас есть только игрок и нужно, чтобы удалить его из своей команды, то вы могли бы иметь статический метод поиска на команды public static Team findTeam(Player player) ...

Я знаю, что это меньше удовлетворения и естественным, чем метод Player#leaveTeam(), но по моему опыту вы все еще можете иметь значимую модель домена.

2 пути ссылка (Родитель -> Ребенок и ляющие> Родитель) часто чреват с другими вещами, скажем, Garbage Collection, сохраняя «ссылочную целостность» и т.д.

Design представляет собой компромисс!

Смежные вопросы