Оновлення (резюме)
Оскільки я написав досить багатослівну відповідь, ось до чого все зводиться:
- Простори імен хороші, використовуйте їх, коли це має сенс
- Використання
inGameIO
та playerIO
заняття, ймовірно, становлять порушення СРП. Це, ймовірно, означає, що ви з'єднуєте спосіб обробки IO з логікою програми.
- Майте пару загальних класів вводу-виводу, які використовуються (або іноді поділяються) класами обробників. Ці класи обробників потім переведуть необроблений вхід у формат, з якого може мати сенс ваша логіка програми.
- Те ж саме стосується і результату: це можна зробити досить загальними класами, але передати стан гри через об'єкт обробника / картографування, який переводить внутрішній стан гри в щось, з чим можуть входити загальні класи IO.
Я думаю, що ти дивишся на це неправильно. Ви відокремлюєте IO у функції компонентів програми, тоді як - на мене, має сенс мати окремі класи IO на основі джерела та "типу" IO.
Маючи кілька базових / загальних KeyboardIO
класів, з яких MouseIO
слід починати, а потім базуватись на тому, коли і де вони вам потрібні, є підкласи, які по-різному обробляють зазначений IO.
Наприклад, введення тексту - це те, що ви, мабуть, хочете по-різному обробити в ігрових елементах управління. Ви побачите, що хочете по-різному зіставити певні ключі залежно від кожного випадку використання, але це відображення не є частиною самого IO, це як ви обробляєте IO.
Дотримуючись SRP, у мене було б кілька класів, які я можу використовувати для клавіатури IO. Залежно від ситуації я, мабуть, захочу по-різному взаємодіяти з цими класами, але їх єдиною роботою є розповісти мені, що робить користувач.
Потім я б вводив ці об'єкти в оброблювальний об'єкт, який би або картував сирий IO на те, з чим може працювати моя логіка програми (наприклад: користувач натискає "w" , обробник відображає це на MOVE_FORWARD
).
Ці обробники, в свою чергу, використовуються для того, щоб змусити героїв рухатися і відповідно малювати екран. Грубе надмірне спрощення, але суть його полягає в такій структурі:
[ IO.Keyboard.InGame ] // generic, if SoC and SRP are strongly adhered to, changing this component should be fairly easy to do
||
==> [ Controls.Keyboard.InGameMapper ]
[ Game.Engine ] <- Controls.Keyboard.InGameMapper
<- IO.Screen
<- ... all sorts of stuff here
InGameMapper.move() //returns MOVE_FORWARD or something
||
==> 1. Game.updateStuff();//do all the things you need to do to move the character in the given direction
2. Game.Screen.SetState(GameState); //translate the game state (inverse handler)
3. IO.Screen.draw();//generate actual output
Зараз у нас є клас, який відповідає за IO клавіатури у своєму сирому вигляді. Ще один клас, який переводить ці дані в щось, що ігровий двигун може насправді мати сенс, ці дані потім використовуються для оновлення стану всіх компонентів, що беруть участь, і, нарешті, окремий клас подбає про вихід на екран.
Кожен клас має одну задачу: обробка вводу клавіатури виконується класом, який не знає / доглядає / повинен знати, що означає вхід, який він обробляє. Все, що він робить - це знати, як отримати вхід (буферизований, небуферований, ...).
Обробник переводить це у внутрішнє представлення для решти програми, щоб зрозуміти цю інформацію.
Ігровий движок бере дані, які були перекладені, і використовує їх для сповіщення всіх відповідних компонентів про те, що щось відбувається. Кожен з цих компонентів виконує лише одне, будь то перевірка зіткнення чи зміна анімації символів, не важливо, це залежить від кожного окремого об'єкта.
Потім ці об'єкти передають свій стан назад, і ці дані передаються Game.Screen
, що по суті є оберненим обробником IO. Він відображає внутрішнє представлення на те, що IO.Screen
компонент може використовувати для отримання фактичного виводу.