Подтвердить что ты не робот

OO дизайн и круговые зависимости

В настоящее время я борюсь с проблемой циклической зависимости при разработке моих классов.

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

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

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

Итак, у нас есть команда, в которой есть список игроков:

public class Team {

    private List<Player> players;

    // snip.

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

Затем у нас есть Player, который имеет ссылку на команду:

public class Player {
    private Team team;

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

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

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

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

public class SomeService {

    public void leave(Player player, Team team) {

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

    }

}

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


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

4b9b3361

Ответ 1

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

В классе 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

Бен,

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

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

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

2 ссылки (родительский → ребенок и ребенок → родитель) часто чреваты другими вещами, например, сбор мусора, сохранение "ссылочной целостности" и т.д.

Дизайн - это компромисс!

Ответ 3

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

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();
    }

}

Ответ 4

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

Тоже внутри leaveTeam.