Задача моего класса по разработке программного обеспечения - разработать приложение, которое может играть в различные формы конкретной игры. Рассматриваемая игра - Манкала, некоторые из этих игр называются Вари или Калах. Эти игры отличаются в некоторых аспектах, но для моего вопроса важно знать, что игры могут отличаться в следующем:
- Способ обработки результата перемещения
- То, как определяется конец игры
- Способ определения победителя
Первое, что мне пришло в голову, чтобы разработать это, это использовать шаблон стратегии, у меня есть вариации в алгоритмах (фактические правила игры). Дизайн может выглядеть так:
Тогда я подумал про себя, что в игре Манкала и Вари способ определения победителя точно такой же, и код будет продублирован. Я не думаю, что это по определению нарушение принципа «одно правило, одно место» или принцип «СУХОЙ», поскольку изменение правил для Mancala не будет автоматически означать, что правило также должно быть изменено в Wari. Тем не менее, по отзывам, полученным от моего профессора, у меня сложилось впечатление найти другой дизайн.
Я тогда придумал это:
Каждая игра (Mancala, Wari, Kalah, ...) будет просто иметь атрибут типа интерфейса каждого правила, то есть, WinnerDeterminer
и если есть версия Mancala 2.0, которая совпадает с Mancala 1.0, за исключением того, как определяется победитель, она может просто используйте версии Mancala.
Я думаю, что реализация этих правил как шаблон стратегии, безусловно, действительна. Но настоящая проблема возникает, когда я хочу разработать его дальше.
Читая о шаблонном методе pattern, я сразу подумал, что его можно применить к этой проблеме. Действия, которые выполняются, когда пользователь делает ход, всегда одинаковы и в одинаковом порядке, а именно:
- кладите камни в лунки (это одинаково для всех игр, поэтому будет реализовано в самом методе шаблона)
- определить результат переезда
- определить, закончилась ли игра из-за предыдущего хода
- если игра закончилась, определите, кто выиграл
Эти три последних шага - все в моей стратегии, описанной выше. У меня много проблем с объединением этих двух. Одним из возможных решений, которое я нашел, было бы отказаться от шаблона стратегии и сделать следующее:
Я действительно не вижу разницы в дизайне между шаблоном стратегии и этим? Но я уверен, что мне нужно использовать шаблонный метод (хотя я был точно так же уверен в необходимости использовать шаблон стратегии).
Я также не могу определить, кто будет отвечать за создание TurnTemplate
объекта, в то время как с помощью шаблона стратегии я чувствую, что у меня есть семейства объектов (три правила), которые я мог бы легко создать, используя абстрактный шаблон фабрики. Я бы тогда иметь MancalaRuleFactory
, WariRuleFactory
и т.д. , и они будут создавать правильные примеры правил и руки меня обратно RuleSet
объект.
Допустим, я использую шаблон стратегии + абстрактная фабрика, и у меня есть RuleSet
объект, в котором есть алгоритмы для трех правил. Единственный способ, которым я чувствую, что все еще могу использовать шаблонный шаблонный метод с этим, состоит в том, чтобы передать этот RuleSet
объект моему TurnTemplate
. Проблема, которая возникает тогда, в том, что мне никогда не понадобятся мои конкретные реализации TurnTemplate
, эти классы устареют. В моих защищенных методах TurnTemplate
я мог просто позвонить ruleSet.determineWinner()
. Как следствие, TurnTemplate
класс больше не будет абстрактным, а должен стать конкретным, тогда он все еще будет шаблоном метода шаблона?
Подводя итог, я правильно думаю, или я что-то упускаю легко? Если я на правильном пути, как мне объединить шаблон стратегии и шаблонный метод?
источник
Ответы:
Посмотрев на ваши дизайны, ваши первые и третьи итерации кажутся более элегантными. Тем не менее, вы упоминаете, что вы студент, и ваш профессор дал вам обратную связь. Не зная точно, каково ваше задание или цель урока или больше информации о том, что предложил ваш профессор, я бы взял все, что я скажу ниже, с долей соли.
В своем первом дизайне вы объявляете
RuleInterface
себя интерфейсом, который определяет, как обрабатывать ход каждого игрока, как определять, закончена ли игра и как определить победителя после ее окончания. Похоже, что это действительный интерфейс для семейства игр, которые испытывают изменения. Однако, в зависимости от игр, вы можете дублировать код. Я бы согласился с тем, что гибкость в изменении правил одной игры - это хорошо, но я бы также сказал, что дублирование кода ужасно для дефектов. Если вы копируете / вставляете дефектный код между реализациями, и в одной из них есть ошибка, теперь у вас есть несколько ошибок, которые необходимо исправить в разных местах. Если вы переписываете реализации в разное время, вы можете вносить дефекты в разных местах. Ни то, ни другое не желательно.Ваш второй дизайн кажется довольно сложным, с глубоким деревом наследования. По крайней мере, это глубже, чем я ожидал бы для решения этого типа проблемы. Вы также начинаете разбивать детали реализации на другие классы. В конечном счете, вы моделируете и внедряете игру. Это может быть интересным подходом, если вам необходимо смешивать и сопоставлять ваши правила для определения результатов хода, конца игры и победителя, что, по-видимому, не соответствует требованиям, которые вы упомянули , Ваши игры - это четко определенные наборы правил, и я постараюсь как можно больше инкапсулировать игры в отдельные объекты.
Твой третий дизайн мне нравится больше всего. Моя единственная проблема в том, что это не на должном уровне абстракции. Прямо сейчас вы, кажется, моделируете поворот. Я бы порекомендовал подумать о разработке игры. Учтите, что у вас есть игроки, которые делают ходы на доске, используя камни. Ваша игра требует присутствия этих актеров. Оттуда ваш алгоритм не является,
doTurn()
ноplayGame()
, который идет от начального хода до последнего хода, после которого он заканчивается. После каждого хода игрока он корректирует состояние игры, определяет, находится ли игра в конечном состоянии, и, если это так, определяет победителя.Я бы порекомендовал присмотреться к вашим первым и третьим проектам и поработать с ними. Это также может помочь мыслить с точки зрения прототипов. Как будут выглядеть клиенты, использующие эти интерфейсы? Есть ли смысл в одном подходе к реализации клиента, который на самом деле собирается создать игру и играть в нее? Вы должны понять, с чем это взаимодействует. В вашем конкретном случае это
Game
класс и любые другие связанные элементы - вы не можете разрабатывать в отдельности.Поскольку вы упомянули, что вы студент, я хотел бы поделиться некоторыми моментами из того времени, когда я был ТА для курса по разработке программного обеспечения:
источник
GameTemplate
который чувствует себя намного лучше. Это также позволяет мне комбинировать фабричный метод для инициализации игроков,Ваше замешательство оправдано. Дело в том, что шаблоны не являются взаимоисключающими.
Шаблонный метод является основой для множества других шаблонов, таких как Стратегия и Состояние. По сути, интерфейс Strategy содержит один или несколько шаблонных методов, каждый из которых требует, чтобы все объекты, реализующие стратегию, имели (как минимум) что-то вроде метода doAction (). Это позволяет заменять стратегии друг на друга.
В Java интерфейс - это не что иное, как набор методов шаблона. Аналогично, любой абстрактный метод по сути является шаблонным методом. Этот образец (среди прочих) был хорошо известен разработчикам языка, поэтому они его встроили.
@ThomasOwens предлагает отличные советы по подходу к вашей конкретной проблеме.
источник
Если вас отвлекают шаблоны проектирования, я бы посоветовал вам сначала создать прототип игры, тогда шаблоны должны просто выпасть на вас. Я не думаю, что действительно возможно или целесообразно пытаться сначала спроектировать систему идеально, а затем внедрить ее (подобным образом я нахожу это озадачивающим, когда люди пытаются сначала написать целые программы, а затем компилировать, а не делать это постепенно) Беда в том, что вряд ли вы будете думать о каждом сценарии, с которым придется сталкиваться вашей логике, и на этапе реализации вы либо потеряете всякую надежду, либо попытаетесь придерживаться своего первоначального некорректного дизайна и внедрять хаки, либо даже хуже ничего не доставлять вообще.
источник
Давайте приступим к медным гвоздям. Нет абсолютно никакой необходимости в каком-либо игровом интерфейсе, шаблонах проектирования, абстрактных классах и UML.
Если у вас есть достаточное количество классов поддержки, таких как пользовательский интерфейс, моделирование и т. Д., То в основном весь ваш код, не относящийся к игровой логике, все равно используется повторно. Более того, ваш пользователь не меняет свою игру динамически. Вы не переворачиваете на 30 Гц между играми. Вы играете в одну игру около получаса. Таким образом, ваш «динамический» полиморфизм на самом деле совсем не динамический. Это довольно статично.
Таким образом, разумный способ пойти сюда - это использовать универсальную функциональную абстракцию, такую как C #
Action
или C ++std::function
, создать один класс Mancala, один Wari и один Kalah и перейти оттуда.Выполнено.
Вы не называете Игры. Игры зовут тебя.
источник