Как применить это ограничение «один ко многим» в C#? - PullRequest
0 голосов
/ 28 февраля 2020

У меня есть 2 класса: коробка и яблоко.

Коробка содержит яблоки. Яблоко может принадлежать только одной коробке. Яблоко должно принадлежать коробке. Каждое яблоко имеет позицию в поле. Это может быть сделано Apple или Box. Яблоко также содержит множество других свойств, не относящихся к Box. Яблоко можно перемещать из одного ящика в другое.

Но как я могу применить приведенные выше ограничения?

Единственная идея, которая у меня есть, заключается в том, что ящик отслеживает положение яблока и также есть словарь stati c, отображающий яблоко на доске, но это не очень хороший дизайн?

Ответы [ 5 ]

2 голосов
/ 28 февраля 2020

Отказ от ответственности: как заявил Габриэль C в своем ответе , идеальный дизайн должен соответствовать принципу единой ответственности. Ваш вопрос, хотя, если интерпретировать его в буквальном смысле, ограничен только двумя классами, которые должны содержать все бизнес-логики c для обработки яблочного бокса. Вот мое решение с этим конкретным c дизайном.

Класс Apple:

public class Apple
{
    public enum AppleKind
    {
        RedDelicious,
        GrannySmith,
        Golden,
        PinkLady
    }

    public AppleKind Kind { get; }
    public Apple(AppleKind kind)
    {
        this.Kind = kind;
    }

    public Box Box { get; private set; }

    public void MoveToBox(Box box, int position)
    {
        if (box == null) throw new ArgumentNullException(nameof(box));
        int oldPosition = -1;
        var oldBox = this.Box;
        if (oldBox != null)
        {
            oldPosition = oldBox.GetApplePosition(this);
            oldBox.RemoveApple(this);
        }
        var moved = false;
        try
        {

            this.Box = box;
            Box.SetApplePosition(this, position);
            moved = true;
        }
        finally
        {
            if (!moved)
            {
                if (oldBox != null)
                    oldBox.AddApple(this, oldPosition);
                else
                    this.Box = null;
            }
        }
    }

    public void MoveToHand()
    {
        this.Box = null;
    }
}

Класс коробки:

public class Box
{
    private Apple[] _apples;

    public IEnumerable<Apple> Apples
    {
        get
        {
            return _apples.Clone() as Apple[];
        }
    }

    public Box(int maxPositions)
    {
        _apples = new Apple[maxPositions];
    }

    public void RemoveApple(Apple apple)
    {
        var position = GetApplePosition(apple);
        if (position > -1)
        {
            _apples[position] = null;
            apple.MoveToHand();
        }
    }

    public int GetApplePosition(Apple apple)
    {
        return Array.IndexOf(_apples, apple);
    }

    public void SetApplePosition(Apple apple, int position)
    {
        // if apple is already in position, do nothing
        if (object.ReferenceEquals(apple, _apples[position])) return;
        _apples[position] = apple;
    }

    public void AddApple(Apple apple, int position)
    {
        if (_apples[position] != null) throw new ArgumentException("Compartment already occupied", nameof(position));
        if (apple == null) throw new ArgumentNullException(nameof(apple));
        if (position < 0 || position > _apples.Length - 1) throw new IndexOutOfRangeException("Cannot add apple outside compartments");
        apple.MoveToBox(this, position);
    }
}

Обратите внимание, что свойство Box.Apples возвращает клон нижележащего массива, предотвращая нарушение клиентским кодом отношения Apple-Box путем манипулирования элементами массива. Apple.Box имеет собственный установщик по той же причине.

Клонировать полный код, включая тесты, по адресу: https://github.com/cvalerio/TossAnAppleToYourWitcher

2 голосов
/ 28 февраля 2020

Я бы создал третий класс, назовем его AppleBoxer. Этот класс будет отвечать за добавление \ удаление \ перемещение яблок из ящиков.

AppleBoxer будет иметь список всех Box, когда вы просите добавить Apple к любому данному Box it перед добавлением проверим, находится ли это яблоко в другом Box.

Свободные \ занятые позиции в Box Я бы оставил в классе Box.

Я думаю, что OOP принципом здесь является Принцип единой ответственности .

Apple не несет ответственности за знание того, в каком ящике он находится.

Box не несет ответственности для того, чтобы знать о яблоках других коробок.

1 голос
/ 28 февраля 2020

Вы можете создать класс ApplePosition, который содержит Apple и позицию. Коробка будет иметь список ApplePosition

0 голосов
/ 28 февраля 2020

Самый простой способ справиться с этим - иметь в классе apple ссылку на поле, к которому он принадлежит. Если вам нужно собрать все яблоки в ящике, все, что вам нужно сделать, - это получить идентификатор ящика и найти все яблоки, которые имеют этот идентификатор в своем справочнике FK_BoxID.

Если яблоко переместится в другой ящик. Все, что вам нужно сделать, это обновить FK_BoxID в классе apple, чтобы переместить его.

См. Пример UML

0 голосов
/ 28 февраля 2020

Я думаю, что простой способ - создать класс AppleBoxManager, который содержит список экземпляров ящика и словарь для сопоставления яблок в ящиках с суррогатными ключами

Суррогатный ключ представляет собой комбинацию boxId + appleId , когда вы добавляете яблоко в поле, добавляете ключ в словарь, когда вы меняете яблоко на другое поле, удаляете ключ и добавляете новый ключ, ключ предотвращает дублирование. Вы должны управлять ошибками logi c при добавлении, удалении или замене методов apple на box. Я полагаю, что все коробки и яблоки имеют уникальный идентификатор.

Аналогичный подход может быть применен в классе коробки с позициями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...