Это нормально, что 2 объекта ссылаются друг на друга? - PullRequest
0 голосов
/ 03 февраля 2019

Я играю в шахматы на C #.У меня есть 2 класса, Поле и Часть:

public class Field
{
    // the piece that is standing on this field
    // null if no piece is standing on it
    public Piece piece { get; set; }
}

public class Piece
{
    // the field this piece is standing on
    public Field field { get; set; }
}

Когда часть перемещается, этот метод вызывается (в классе Часть):

public void Move(Field field)
{
    this.field = field;
    field.piece = this;
}

Это не похоже набыть хорошим кодированием, потому что каждый раз, когда я изменяю свойство поля, мне также приходится менять свойство куска для этого поля.Тем не менее, мне нужны оба свойства, потому что где-то в моем коде они оба нужны для выполнения проверок и т. Д. (Например, в каком поле находится этот фрагмент и в каком фрагменте берется это поле).

Мой вопрос:это совершенно нормально, это плохой запах кода или это совершенно неправильно?Что было бы хорошим решением для решения этой проблемы?

Любой совет?Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 03 февраля 2019

Нет! Это относится к понятию круговой зависимости .Несмотря на то, что он применяется для модулей, его вполне можно рассматривать как предшественник таких.

Более конкретно, это идеальный пример для взаимно рекурсивных объектов.Концептуально, если вы подставите (полупсевдокод)

public class Field
{
    public Piece piece {
        public Field field {
            public Piece piece {
                ...
            }
        }
    }
}

Это потому, что объекты определены в терминах друг друга.Тогда теоретически вы можете сделать что-то вроде

this.field.piece.field.piece...
0 голосов
/ 03 февраля 2019

Проблема, которую я вижу здесь, состоит в том, что у вас есть Piece.field и Field.piece в качестве общедоступных свойств.Это означает, что другие могут устанавливать эти свойства без обновления соответствующего.

Кроме того, когда вы перемещаете фигуру из одного поля в другое, вы не удаляете фигуру из предыдущего поля, и мы позволяем фигурампереместиться на занятые квадраты, что приведет к тому, что несколько фрагментов будут ссылаться на одно и то же поле, но поле будет ссылаться только на последний фрагмент, размещенный там.приватный установщик), заставляя клиентов вызывать соответствующий метод Set или Move для их изменения.Затем в этом методе мы можем убедиться, что поле, в которое мы перемещаемся, не занято (если оно есть, мы просто выкидываем исключение - клиент должен сначала проверить это перед вызовом Move), и что мы очищаем Piece из Field, из которого мы переехали.

Работу по проверке можно выполнить в классе Field или Piece, либо в обоих.Я помещаю все это в класс Field, чтобы упростить вещи.

Тем не менее, есть проблемы с этим.Вы можете вызвать Field.SetPiece(piece) напрямую (вместо Piece.MoveTo(field);), что оставит кусок со значением null для Field.Так что это лишь небольшое улучшение, но не идеальное решение.См. Ниже для лучшей идеи.

public class Field
{
    public Piece Piece { get; private set; }
    public bool Occupied => Piece != null;

    public void ClearPiece()
    {
        // Remove this field from the piece
        if (Piece?.Field == this) Piece.MoveTo(null);

        // Remove the piece from this field
        Piece = null;
    }

    public void SetPiece(Piece piece)
    {
        if (piece != null)
        {
            if (Occupied)
            {
                throw new InvalidOperationException(
                    $"Field is already occupied by {Piece}.");
            }

            // Remove piece from the piece's previous field
            if (piece.Field?.Piece == piece)
            {
                piece.Field.ClearPiece();
            }
        }

        Piece = piece;
    }
}

public class Piece
{
    public Field Field { get; private set; }

    public void MoveTo(Field field)
    {
        field.SetPiece(this);
        Field = field;
    }
}

Подумав немного больше об этом, я думаю, что лучшее решение было бы иметь класс GameManager, который обрабатывает все проверки и перемещения, итогда мы можем сделать классы Field и Piece "немыми".

Это имеет смысл, поскольку перед установкой Piece для Field необходимо выполнить гораздо больше проверок.Можно ли переместить эту фигуру в локацию (т. Е. Если король находится под контролем, и это не блокирует его, то это не разрешено).Является ли Field допустимым местом приземления для фигуры на основе правил перемещения фигуры (то есть горизонтальное положение для слона не допускается)?Есть ли что-нибудь, блокирующее путь части, чтобы добраться до места назначения?Занят ли пункт назначения другой частью, принадлежащей тому же игроку?Много вещей для оценки перед перемещением фигуры.

Кроме того, это позволит нам повторно использовать классы Piece и Field в других типах игр, которые могут иметь другой набор правил и другойGameManager чтобы обеспечить их соблюдение.

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