Однажды я задал вопрос о переполнении стека о наследовании.
Я сказал, что проектирую шахматный движок в ООП-моде. Таким образом, я наследую все свои части от абстрактного класса Piece, но наследование все еще продолжается. Позвольте мне показать по коду
public abstract class Piece
{
public void MakeMove();
public void TakeBackMove();
}
public abstract class Pawn: Piece {}
public class WhitePawn :Pawn {}
public class BlackPawn:Pawn {}
Программисты посчитали мой дизайн немного более инженерным и предложили убрать цветные классы элементов и сохранить их в качестве элемента свойства, как показано ниже.
public abstract class Piece
{
public Color Color { get; set; }
public abstract void MakeMove();
public abstract void TakeBackMove();
}
Таким образом, Пьеса может знать его цвет. После реализации я увидел, что моя реализация идет так, как показано ниже.
public abstract class Pawn: Piece
{
public override void MakeMove()
{
if (this.Color == Color.White)
{
}
else
{
}
}
public override void TakeBackMove()
{
if (this.Color == Color.White)
{
}
else
{
}
}
}
Теперь я вижу, что свойство color вызывает операторы if в реализациях. Это заставляет меня чувствовать, что нам нужны определенные кусочки цвета в иерархии наследования.
В таком случае у вас будут классы, такие как WhitePawn, BlackPawn, или вы будете заниматься дизайном, сохраняя свойство Color?
Не видя такой проблемы, как бы вы хотели начать дизайн? Сохранять свойство Color или иметь наследственное решение?
Изменить: я хочу указать, что мой пример может не полностью соответствовать примеру реальной жизни. Поэтому, прежде чем пытаться угадать детали реализации, просто сконцентрируйте вопрос.
Я на самом деле просто спрашиваю, приведет ли использование свойства Color, если заявления лучше использовать наследование?
Ответы:
Представьте, что вы разрабатываете приложение, которое содержит транспортные средства. Вы создаете что-то подобное?
В реальной жизни цвет является свойством объекта (автомобиля или шахматной фигуры) и должен быть представлен свойством.
Существуют
MakeMove
иTakeBackMove
разные для черных и белых? Совсем нет: правила одинаковы для обоих игроков, что означает, что эти два метода будут абсолютно одинаковыми для черных и белых , что означает, что вы добавляете условие, когда оно вам не нужно.С другой стороны, если у вас есть
WhitePawn
иBlackPawn
я не удивлюсь, если рано или поздно вы напишите:или даже что-то вроде:
источник
direction
свойство и использовать его для перемещения, чем использовать свойство color. Цвет в идеале должен использоваться только для рендеринга, а не для логики.direction
свойство является логическим. Я не вижу, что с этим не так. Что меня смутило в этом вопросе до комментария Фрешблада выше, так это то, почему он хотел бы изменить логику движения в зависимости от цвета. Я бы сказал, что это сложнее понять, потому что цвет не имеет смысла влиять на поведение. Если все, что вы хотите сделать, это определить, какой игрок владеет какой частью, вы можете поместить их в две отдельные коллекции и избежать необходимости в собственности или наследовании. Сами фигуры могут блаженно не знать, к какому игроку они принадлежат.direction
или, возможно,player
свойство вместоcolor
логики в игре.white castle's moves differ from black's
- как? Вы имеете в виду рокировку? Рокировка на стороне короля и на стороне королевы на 100% симметричны для черного и белого.Вы должны спросить себя, почему вам действительно нужны эти утверждения в вашем классе Pawn:
Я уверен, что этого будет достаточно, чтобы иметь небольшой набор функций, таких как
в своем классе Piece и реализуйте все функции в
Pawn
(и других производныхPiece
), используя эти функции, не имея ни одного из этихif
утверждений выше. Единственным исключением может быть функция определения направления перемещения пешки по ее цвету, поскольку это характерно для пешек, но почти во всех других случаях вам не понадобится какой-либо специфичный для цвета код.Другой интересный вопрос, если у вас действительно должны быть разные подклассы для разных видов пьес вообще. Это кажется мне разумным и естественным, поскольку правила для ходов для каждой фигуры различны, и вы наверняка можете реализовать это, используя разные перегруженные функции для каждой фигуры.
Конечно, можно было бы попытаться смоделировать это в более общем виде, путем разделения свойств деталей из их движущихся правил, но это может закончиться в абстрактном классе
MovingRule
и иерархии подклассаMovingRulePawn
,MovingRuleQueen
... Я бы не начать его , что Кстати, так как это может легко привести к другой форме чрезмерного проектирования.источник
Наследование не так полезно, когда расширение не влияет на внешние возможности объекта .
Свойство Цвет не влияет , как объект Кусок используется . Например, все фигуры могут вызывать метод moveForward или moveBackwards (даже если перемещение не разрешено), но инкапсулированная логика пьесы использует свойство Color для определения абсолютного значения, которое представляет движение вперед или назад (-1 или + 1 по оси y), что касается вашего API, ваш суперкласс и подклассы являются идентичными объектами (так как все они предоставляют одни и те же методы).
Лично я бы определил некоторые константы, представляющие каждый тип фрагмента, а затем использовал бы оператор switch для перехода к частным методам, которые возвращают возможные перемещения для экземпляра «this».
источник
Board
Класс (например) может быть ответственными за преобразование этих концепций в -1 или +1 , в зависимости от цвета части.Лично я бы вообще ничего не унаследовал
Piece
, потому что не думаю, что ты что-то получаешь от этого. Предположительно, фигуре на самом деле все равно, как она движется, только какие квадраты являются допустимым местом для ее следующего хода.Возможно, вы могли бы создать Калькулятор Действительного Движения, который знает доступные шаблоны движения для типа фигуры и может получить список допустимых квадратов назначения, например
источник
Piece
». Кажется, что Пайс отвечает не только за вычисление ходов, что является причиной разделения движения как отдельного вопроса. Определение того, какой кусок получит, какой калькулятор будет зависеть от внедрения зависимости, напримерvar my_knight = new Piece(new KnightMoveCalculator())
В этом ответе я предполагаю, что под «шахматным движком» автор вопроса подразумевает «шахматный ИИ», а не просто механизм проверки ходов.
Я думаю, что использование ООП для фигур и их правильных движений - плохая идея по двум причинам:
Отойти от соображений производительности, включая цвет фигуры в иерархии типов, на мой взгляд, плохая идея. Вы окажетесь в ситуациях, когда вам нужно проверить, имеют ли две части разные цвета, например, для захвата. Проверка этого условия легко выполняется с помощью свойства. Делать это, используя
is WhitePiece
-tests, было бы ужаснее по сравнению.Существует также еще одна практическая причина, по которой следует избегать генерации ходов для методов, специфичных для фигуры, а именно, что разные фигуры имеют общие ходы, например ладьи и королевы. Чтобы избежать дублирования кода, вам нужно будет разложить общий код на базовый метод и сделать еще один дорогостоящий вызов.
При этом, если вы пишете первый шахматный движок, я бы определенно посоветовал придерживаться принципов чистого программирования и избегать приемов оптимизации, описанных автором Crafty. Разобраться со спецификой шахматных правил (рокировка, повышение по службе, en-passant) и сложными техниками искусственного интеллекта (хеш-доски, вырезка альфа-бета) достаточно сложно.
источник
WhichMovesArePossible
является разумным подходом.