Назначение ответственности: игра, в которой есть игроки, у каждого из которых есть банковский счет.

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

Но этот маленький сценарий продолжает беспокоить меня.

Допустим, я разрабатываю простую настольную игру. У меня есть класс Game, который содержит информацию о доске и игроках. Игроки создаются из класса Player. У всех игроков есть банковский счет, созданный из класса BankAccount.

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

My BankAccount имеет методы снятия, внесения и возврата баланса. Но моя игра не знает о банковском счете игрока. Только про плеер. Дать ли моему классу Player метод getAccount, чтобы я мог вызывать playerObject.getAccount().withdraw()? Или как мне возложить ответственность за такое поведение? Должен ли мой класс Player реализовать метод withdraw(), который просто вызывает account.withdraw() для связанного с ним объекта учетной записи?


person leflings    schedule 31.10.2011    source источник


Ответы (4)


Bank должен иметь контроль над BankAccounts, а не над player объектами.

Другими словами, банк должен быть набором банковских счетов плюс некоторые дополнительные методы для управления этими счетами.

Это так, что вы можете делать такие вещи, как:

bank.WithDrawMoney(player, amount);

or

bank.TransferMoney(playerFrom, playerTo, amount);

В этом случае банк «найдет» счет, который есть у этого игрока, и удалит с него соответствующие средства.

Представьте, если бы это был настоящий банковский счет. Человек не носит с собой этот счет, только ссылку на него. Банк несет ответственность за перевод средств (депозиты, снятие средств...).

Итак, на верхнем уровне у вас будет Game, который содержит такие объекты, как Players, Bank и Board. У него могут быть лучшие методы, такие как RollDice(). Совет может иметь такой метод, как Move(player, distance);

person NotMe    schedule 31.10.2011
comment
В более общем виде я вижу, что это хорошая идея. Но я думаю, что для этого конкретного приложения это только увеличит сложность до нежелательного уровня? У меня были бы те же вопросы, если бы у игрока было домашнее животное, и игровой контроллер должен был что-то сделать с домашним животным. - person leflings; 31.10.2011
comment
+1. При желании вы можете полностью отделить банк от игрока и передать в методы банка только имя (для этого простого сценария) или accountID. - person TrueWill; 31.10.2011
comment
Читая это во второй раз, это действительно хорошо для меня. Это общий принцип проектирования? И если да, то носит ли он определенное имя? - person leflings; 01.11.2011
comment
@leflings: не уверен, что у него есть имя. Названия принципов дизайна никогда не имели для меня значения, поскольку здесь важнее сам дизайн: понятен ли он? Отвечает ли это потребностям приложения? Это ремонтопригодно? В тех областях, которые имеют значение, является ли он должным образом расширяемым? Думаю, я занимаюсь этим намного дольше, чем у большинства принципов дизайна есть названия. - person NotMe; 03.11.2011

playerObject.getAccount().withdraw() нарушает "Закон" Деметры.

Предоставьте методы Player и т. д., которые имеют смысл для игрока. Изучите инверсию управления для работы с зависимостями.

person TrueWill    schedule 31.10.2011

Я думаю, вы хотите задать себе этот вопрос: является ли учетная запись, которой владеет игрок, публичной собственностью игрока? Если да, возможно, getAccount() — неплохой вариант.

В конце концов все сводится к балансу. Если класс вашей учетной записи имеет 20 методов/свойств (давайте проигнорируем аргументы о том, является ли 20 большим числом), то очевидно, что с точки зрения кодирования наличие одной функции player.getAccount() будет дублировать намного меньше кода, чем наличие 20 функций в проигрывателе. которые просто переадресовывают звонки на счет.

С другой стороны, вы должны помнить, что getAccount() теперь связывает реализацию вашего игрового класса с тем фактом, что у каждого игрока должна быть учетная запись a. У вас не может быть игроков, у которых нет банковских счетов.

В лучшей стратегии OO вы хотите, чтобы каждый класс служил своего рода абстракцией, чтобы он предоставлял упрощенное представление своим клиентам, скрывая сложность/детали за частными переменными. Вы не делаете этого, если у вас есть getAccount(), но вы также не делаете этого, если вы просто предоставляете каждую функцию учетной записи в классе игрока.

Поэтому вместо этого вы могли бы иметь класс игрока просто с функцией getCash(). Снаружи (то есть с точки зрения игрового класса) не имеет значения, как игрок получает деньги. Внутри игрок может перейти на банковский счет и выполнить снятие средств. Но, сохраняя две вещи разделенными, позже у вас может быть другой класс игроков, который может получить деньги каким-то другим способом (например, добраться до своего собственного кошелька). Суть только в том, чтобы выставить наружу поведение самого игрока и скрыть, как это поведение реализовано (т.е. не афишировать, что деньги сняты с банковского счета).

person DXM    schedule 31.10.2011
comment
Это довольно близко к моим мыслям по этому поводу, вы просто лучше выразили это словами, чем я :) Было бы плохой идеей добавить подкласс к Player, PlayerWithBankAccount? - person leflings; 31.10.2011
comment
добавление подкласса может быть немного преждевременным, если позже вы добавите другое свойство и будете следовать той же логике дизайна, вы можете закончить что-то вроде PlayerWithOneMissingLeg, но что тогда произойдет, если ему также понадобится банковский счет... PlayerWithBankAccountAndOnlyOneLeg? Я бы посоветовал не усложнять его до тех пор, пока вы не почувствуете, что есть конкретная причина для разделения класса player (например, он может стать слишком большим), и в этот момент решите, как лучше всего провести его рефакторинг (кстати, «Рефакторинг» — отличная книга, которую я очень рекомендую). - person DXM; 31.10.2011

Что бы это ни стоило, я обычно считаю продуктивным попытаться начать думать в проблемной области, а не в коде, как это было затронуто другими.

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

Я понимаю, что вы заявили, что у вас есть класс банковского счета, но если процесс заключается в том, что Джо Блоггс действительно платит наличными (из своего тайника в бачке унитаза) Фреду, не имея никакого отношения к банку, тогда он не собирается нужен счет в банке. Если затем Фред решит в какой-то момент положить его в банк, а не продолжать запихивать его под матрас (или игровую доску), то в этот момент ему понадобится банк и счет.

Предполагая, что player может получить/получить доступ к другим игрокам, первый процесс увидит (в Player) что-то вроде:

void payPlayer( player, amount )
{
  spendCash( amount ); // reduces stash by "amount"
  player.recieveCash( amount );

}

Размер тайника с деньгами может быть просто атрибутом класса игрока.

Второй вид деятельности — это совсем другое. Теперь Фред должен сначала сообщить своему банку, что он хочет внести 100 долларов, а затем его банк либо через дырку в стене, либо через кассира (надеюсь) кредитует его Счет. Так что Фреду сейчас действительно нужен банк и, возможно, счет. Но зачем ему нужна учетная запись? Какой цели он служит в игре? Это обязательно необходимый компонент программы или он просто есть, потому что в реальной жизни деньги уходят в банки?

Кроме того, есть случай, когда игрок действительно хочет перевести банковские средства со своего счета на счет другого игрока в качестве платежа. В этом сценарии обоим игрокам нужен банк и счет, но только банк может фактически переводить средства, поэтому для этого им просто нужно знать о банке. Конечно, это можно было бы сделать только в том случае, если бы у банка была какая-то другая, значительная роль в игре.

Так что ответ, как реализовать перевод денег в игре, полностью зависит от игровой механики. Если вам действительно нужен класс, похожий на банковский счет, то можно с уверенностью сказать, что у вас должен быть банк. Но вы должны спросить себя, зачем вам вообще нужны летающие вокруг банковские счета? И если банковский счет игрока напрямую связан с ним, то действительно ли это больше банковский счет или просто Джо опускает руку в бачок унитаза?

person Mike G    schedule 28.02.2012