2010-04-23 2 views
10

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

Сначала я передавал соответствующие объекты методам, как их называли, но это становилось очень утомительным, особенно когда методам требовалось много объектов для выполнения своих задач. Чтобы решить эту проблему, я создал класс, который инициализирует и сохраняет все необходимые мне объекты. Это позволяет мне получить доступ к объекту из любого класса, вызвав, например, Assets.dice().

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

ответ

7

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

Я думаю, что лучше создать «Контекст», содержащий текущие кости, куски и т. Д. И т. Д., И передать контекст по мере необходимости методам/классам, которые должны его использовать. Это намного чище, хотя да, это боль, которую нужно пройти по всему миру. Но вы получаете преимущество в том, что вы можете отслеживать, кто имеет доступ к Контексту, когда, а также вы можете создавать mock Contexts для целей тестирования. Если вы передаете контекст в объект высокого уровня, и он должен передать его своим субкомпонентам, вы знаете, от начала до конца, что такое контекст и откуда он пришел.

Кроме того, в идеале приятно сделать Контекст неизменным. Это может быть невозможно. Но если для каждого заданного поворота, если вы можете создать новый Контекст, который фиксирует текущее состояние и неизменен, вы уменьшаете еще больше сюрпризов от вашего приложения.

+0

Это не синглтон, поскольку доступ осуществляется через статические интерфейсы. Но он страдает от многих из тех же недостатков, поэтому я воздержусь от маркировки. – DJClayworth

+0

Спасибо Мэтт. Я изменил код, так что только основной файл логики создает и имеет прямой доступ к объекту GameAssets. Затем это передается другим классам/методам. Это все еще кажется неправильным, возможно, потому, что моя логика сверху вниз по дизайну. Я в конечном итоге передаю этот объект повсюду. Но, возможно, это другой вопрос сам по себе - как разбить игровую логику на мелкие биты? Мне нужно подумать об этом. – Glitch

0

Действительно ли это «божественный» класс или просто «контекст», который является «контейнером» для всех связанных между собой экземпляров объектов одной игры, которые затем передаются другим вызовам метода (так что вы просто один аргумент)? Последнее довольно распространено, и я не вижу в этом ничего плохого, но в этом случае сам контейнер не имеет реальной функциональности.

0

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

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

2

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

Одно руководство, что помогло мне понять этот материал представляет собой диалог между двумя антропоморфными классами (если кто-то знает оригинальный источник этой цитаты, я оценил бы ссылку!):

класса А говорит Класс B: «Дайте мне значение x».

Класс B: «Почему вы хотите значение x?"

Класс A:„Таким образом, я могу фланец его“

Класса B:„. Спросите меня, и я фланец для Вас“

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

Другая вещь, которую вы можете захотеть взглянуть на некоторые общие объектно-ориентированный Design Patterns Что-то вроде игровых кубиков m что имеет смысл как Singleton, так как вам не нужно больше одного экземпляра.

Если вы хотите получить хорошее представление о Design Patterns, я бы рекомендовал собрать отличную книгу Head First Design Patterns.

+0

Вопрос о том, кто должен использовать фланец B, является более сложным, чем вы указали, и в основе хорошего дизайна. Не указано, что B должен фланцевать свои собственные данные. Например: кто хочет получить результат? И класс C фланца своими собственными данными так же? – DJClayworth

+0

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

+0

+1 ... Некоторые люди были намного дальше этого и утверждают, что само присутствие * getters * доказывает, что вы не делаете OO.Почти * каждый *, * везде * использует геттеры, поэтому люди будут сильно не соглашаться (потому что людям не нравится, когда они чувствуют, что «лучшая практика», с которой они следуют с 15 лет, «атакованы»). Но это увлекательная мысль. Когда я читаю об этом, вместо того, чтобы думать * «глупо тебя» **, я все время использую геттеры, я знаю, что геттеры - это все! »Я думал *« О, это интересно »*:) – SyntaxT3rr0r

0

Наличие такого класса контекста, который имеет доступ ко всему, очень похож на глобальные переменные. Имеются те же недостатки. Глобальную переменную можно читать и изменять любым способом. Это сопрягает все, что использует глобальную переменную друг к другу. Связь плохая, потому что, когда вещи связаны, изменение одного объекта может вызвать что-то в другом объекте. Когда степень сцепления возрастает, становится сложно справляться с сложностью (бабочка, хлопающая по крыльям в нашем классе игрока, может вызвать исключение в вашем классе кубиков). Изменение и дополнительное развитие усложняются. Трудно обнаружить и скрывать ошибки становятся неизбежными.

Так, например, однажды, тестируя приложение, вы можете заметить, что ваш объект кости играет странно. Вы вызываете dice.roll() и видите, что он возвращает 1 иногда. Но это невозможно в этом случае, потому что ваш игрок перекатывает два из них. Вы отлаживаете и как-то замечаете, что свойство numberOfDice в какой-то момент изменилось на 1. С помощью метода контекста, подобного вашему, нелегко будет найти, кто изменил числоOfDice на 1, потому что каждый имеет доступ к кубикам через ваш объект контекста. Вы понимаете?

Так в чем же решение? Одна из метафор, которые мне нравятся для программирования OO, разделяет и побеждает. Вам нужно найти поведение, процессы, объекты, которые могут быть изолированы друг от друга. Вам нужно разделить проблему на управляемые части, которые могут быть изолированы от остальной части материала в вашем приложении.

Изучение того, как это сделать, конечно, нелегко и требует много изучения, чтения, мышления, обсуждения и, конечно, кодирования.