Я программировал версию программного обеспечения настольной игры. К настоящему времени я записал классы, которые будут соответствовать физическим объектам на игровой доске. Я хорошо в запись логики программы, однако я нашел, что многие логические классы требуют доступа к тем же объектам.
Сначала я передавал соответствующие объекты методам, как их назвали, но это становилось очень утомительным, особенно когда методы потребовали многих объектов выполнить их задачи. Для решения этого я создал класс, который инициализирует и хранит все объекты, в которых я нуждаюсь. Это позволяет мне получать доступ к объекту от любого класса путем вызова Assets.dice (), например.
Но теперь, когда я думал об этом, это не кажется правильным. Поэтому я здесь, я боюсь, что создал своего рода класс бога. Действительно ли этот страх необоснован, или я создал залог провала?
Вы в основном столкнулись с шаблоном singleton. По большей части это плохой образец. Предоставляя любой части вашего приложения доступ к практически глобальным данным, подобным этому, практически в любое время, вы в конечном итоге получаете спагетти из кода, который сложно поддерживать, отлаживать и, самое главное, тестировать.
Я думаю, что лучше создать «Контекст», содержащий текущие игральные кости, части и т.д., и передавать контекст по мере необходимости методам / классам, которые должны его использовать. Это намного чище, хотя да, это больно, когда приходится проходить его повсюду. Но вы получаете то преимущество, что вы можете отслеживать, кто и когда обращается к контексту, а также можете создавать фиктивные контексты для целей тестирования. Если вы передаете контекст высокоуровневому объекту, а он должен передать его своим подкомпонентам, вы от начала до конца знаете, что это за контекст и откуда он.
Кроме того, в идеале было бы неплохо сделать контекст неизменным. Это может быть невозможно. Но если для каждого данного хода вы можете создать новый контекст, который фиксирует текущее состояние и является неизменным, вы уменьшите еще большее удивление от своего приложения.
Наличие такого рода класса контекста, имеющего доступ ко всему, очень похоже на наличие глобальных переменных. Есть те же недостатки. Глобальную переменную можно прочитать и изменить любым способом. Это связывает все, что использует глобальную переменную, друг с другом. Связь - это плохо, потому что, когда вещи связаны, изменение одного объекта может вызвать что-то в другом объекте. Когда степень сцепления увеличивается, становится очень трудно управлять сложностью (хлопающая крыльями бабочка в нашем классе игроков может вызвать исключение в вашем классе игральных костей). Менять и дорабатывать становится все труднее. Ошибки, которые трудно обнаружить и скрыть, становятся неизбежными.
Так, например, однажды во время тестирования вашего приложения вы могли заметить, что ваш объект игральных костей ведет себя странно. Вы вызываете dice.roll () и иногда видите, что он возвращает 1. Но в данном случае это невозможно, потому что ваш игрок бросает два из них. Вы отлаживаете и каким-то образом замечаете, что свойство numberOfDice в какой-то момент изменилось на 1. С помощью такого метода контекста, как ваш, будет непросто найти, кто изменил numberOfDice на 1, потому что у всех есть доступ к играм в кости через ваш объект контекста. Вы уловили суть?
Так каково же решение? Одна из метафор, которая мне нравится в объектно-ориентированном программировании - разделяй и властвуй. Вам нужно найти поведения, процессы, объекты, которые можно изолировать друг от друга. Вам нужно разделить вашу проблему на управляемые части, которые можно изолировать от всего остального, что происходит в вашем приложении.
Научиться делать это, конечно, непросто и требует много изучения, чтения, размышлений, обсуждений и, конечно же, программирования.
Похоже, вы спрашиваете об общей философии объектно-ориентированного программирования. В общем, вы обнаружите, что моделирование реальных объектов в классы не всегда имеет наилучший смысл для вашего кода.
Одно из указаний, которое помогло мне разобраться в этом вопросе, - это диалог между двумя антропоморфными классами (если кто-то знает первоисточник этой цитаты, я был бы признателен за ссылку!):
Класс A говорит классу B: " Дайте мне значение x ».
Класс B:« Зачем вам нужно значение x? »
Класс A:« Чтобы я мог его фланцевать »
Класс B:« Спросите меня, и Я сделаю это для вас ».
Это помогает понять, что класс предназначен для инкапсуляции данных , а выполняет с ними манипуляции.В общем, это притча, которая помогла мне лучше организовать мой код.
Еще одна вещь, на которую вы, возможно, захотите обратить внимание, - это некоторые общие объектно-ориентированные шаблоны проектирования . Что-то вроде игровых кубиков может иметь больше смысла в качестве Синглтона , поскольку вам не нужно более одного его экземпляра.
Если вы хотите получить хорошее представление о шаблонах проектирования, я бы порекомендовал взять отличную книгу Head First Design Patterns .
Действительно ли это класс «бога» или просто «контекст», который является «контейнером» для всех этих взаимосвязанных экземпляров объектов одной игры, которые затем передаются в вызовы разных методов (так что у вас есть только один аргумент)? Последнее довольно распространено, и я не вижу в этом ничего плохого, но в этом случае сам контейнер не имеет реальной функциональности.
Спасибо за этот вопрос. Некоторое время я задавался вопросом об одной и той же проблеме. Передача объектов через метод и ограничение параметров требует создания классов, которые могут содержать все эти объекты.
Тем не менее, я думаю, что дизайн неплох, так как вам нужен класс Domain, который проходит через несколько уровней. Если этот класс домена не несет необходимых объектов и не выполняет никакой логики, все должно быть в порядке.