Например, предположим, что у вас есть консольная игровая программа, которая имеет все виды методов ввода / вывода в консоль и из консоли. Будет ли это быть умным , чтобы держать их все в одном inputOutput
классе или разбить их на более конкретных классы , как startMenuIO
, inGameIO
, playerIO
, gameBoardIO
и т.д. , так что каждый класс имеет около 1-5 методов?
И в то же время, если лучше разбить их на части, было бы разумно поместить их в IO
пространство имен, сделав их более многословными, например: IO.inGame
и т. Д.?
Ответы:
Обновление (резюме)
Поскольку я написал довольно многословный ответ, вот что все сводится к следующему:
inGameIO
иplayerIO
классы, скорее всего , представляют собой нарушение SRP. Это, вероятно, означает, что вы связываете способ обработки ввода-вывода с логикой приложения.Я думаю, что вы смотрите на это неправильно. Вы отделяете IO в зависимости от компонентов приложения, тогда как для меня более разумно иметь отдельные классы IO, основанные на источнике и «типе» IO.
Имея несколько базовых / универсальных
KeyboardIO
классовMouseIO
для начала, а затем основываясь на том, когда и где они вам нужны, есть подклассы, которые по-разному обрабатывают указанный IO.Например, ввод текста - это то, что вы, вероятно, хотите обрабатывать иначе, чем внутриигровые элементы управления. Вы обнаружите, что хотите отображать определенные ключи по-разному в зависимости от каждого варианта использования, но это сопоставление не является частью самого IO, это то, как вы обрабатываете IO.
Придерживаясь SRP, у меня будет пара классов, которые я могу использовать для ввода-вывода клавиатуры. В зависимости от ситуации, я, вероятно, захочу по-разному взаимодействовать с этими классами, но их единственная задача - рассказать мне, что делает пользователь.
Затем я вставил бы эти объекты в объект-обработчик, который либо отобразил бы необработанный ввод-вывод на что-то, с чем могла бы работать логика моего приложения (например: пользователь нажимает «w» , обработчик отображает это на
MOVE_FORWARD
).Эти обработчики, в свою очередь, используются, чтобы заставить персонажей двигаться, и соответственно рисовать экран. Грубое упрощение, но суть его в следующем:
Теперь у нас есть класс, который отвечает за ввод-вывод клавиатуры в необработанном виде. Другой класс, который переводит эти данные во что-то, что игровой движок действительно может иметь смысл, эти данные затем используются для обновления состояния всех задействованных компонентов, и, наконец, отдельный класс позаботится о выводе на экран.
У каждого отдельного класса есть одно задание: обработка ввода с клавиатуры выполняется классом, который не знает / не заботится / должен знать, что означает ввод, который он обрабатывает. Все, что он делает, это знает, как получить входные данные (буферизованные, небуферизованные, ...).
Обработчик переводит это во внутреннее представление для остальной части приложения, чтобы понять эту информацию.
Движок игры берет переведенные данные и использует их для уведомления всех соответствующих компонентов о том, что происходит. Каждый из этих компонентов делает только одну вещь, будь то проверка столкновений или изменение анимации персонажа, это не имеет значения, это зависит от каждого отдельного объекта.
Эти объекты затем возвращают свое состояние обратно, и эти данные передаются
Game.Screen
, что по сути является обратным обработчиком ввода-вывода. Он отображает внутреннее представление на то, чтоIO.Screen
компонент может использовать для генерации фактического вывода.источник
IO
иgame
пространства имен или классов с подклассами?Принцип единой ответственности может быть сложно понять. Что я нашел полезным, так это думать о том, как ты пишешь предложения. Вы не пытаетесь втиснуть много идей в одно предложение. Каждое предложение должно четко сформулировать одну идею и отложить детали. Например, если вы хотите определить автомобиль, вы бы сказали:
Затем вы можете определить такие вещи, как «транспортное средство», «дорога», «колеса» и т. Д. Отдельно. Вы бы не попытались сказать:
Точно так же вы должны попытаться сделать ваши классы, методы и т. Д. Как можно проще изложить центральную концепцию и передать детали другим методам и классам. Как и при написании предложений, нет строгих правил относительно того, насколько большими они должны быть.
источник
Я бы сказал, что лучше всего держать их в отдельных классах. Маленькие классы не плохие, на самом деле большую часть времени они являются хорошей идеей.
Что касается вашего конкретного случая, я думаю, что разделение может помочь вам изменить логику любого из этих конкретных обработчиков, не затрагивая другие, и, при необходимости, вам будет проще добавить новый метод ввода / вывода, если он придет к нему.
источник
Принцип единой ответственности гласит, что у класса должна быть только одна причина для изменения. Если у вашего класса есть несколько причин для изменения, вы можете разделить его на другие классы и использовать композицию для устранения этой проблемы.
Чтобы ответить на ваш вопрос, я должен задать вам вопрос: есть ли у вашего класса только одна причина для изменения? Если нет, то не бойтесь добавлять более специализированные классы, пока у каждого из них только одна причина для изменения.
источник