Подтвердить что ты не робот

Работа с "глобальными" структурами данных в объектно-ориентированном мире

Это вопрос со многими ответами - мне интересно узнать, что другие считают "лучшей практикой".

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

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

  • Вы можете поместить все структуры данных внутри одного объекта и передать ссылки на этот объект. Это может быть либо объект, созданный именно для этой цели, либо он может быть "основным" объектом вашей программы. Это упрощает проблемы (1), но структуры данных могут или не могут иметь ничего общего друг с другом, и сбор их вместе в одном объекте довольно произволен.

  • Вы можете сделать структуры данных "статическими". Это позволяет вам ссылаться на них непосредственно из других классов, без необходимости передавать ссылки. Это полностью исключает недостатки (1), но явно не OO. Это также означает, что может быть только один экземпляр программы.

Когда есть много структур данных, все из которых требуется для множества классов, я склонен использовать (2). Это компромисс между чистотой и практичностью OO. Что делают другие люди? (Для чего это стоит, я в основном из Java-мира, но это обсуждение применимо к любому языку OO.)

4b9b3361

Ответ 1

Глобальные данные не так плохи, как утверждают многие пуристы OO!

В конце концов, при реализации классов OO вы обычно используете API для своей ОС. Что это за черт, если это не огромная куча глобальных данных и услуг!

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

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

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

Ответ 2

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

Таким образом, по моему опыту, если вы делаете 3), вы в конечном итоге будете делать 1) с удвоенной стоимостью.

Перейти на 1 и быть мелкозернистым относительно того, какие структуры данных вы ссылаетесь на каждый объект. Не используйте "объекты контекста", просто передавайте именно нужные данные. Да, это делает код более сложным, но с положительной стороны он делает его более ясным - тот факт, что a FwurzleDigestionListener содержит ссылку как на Fwurzle, так и на DigestionTract, сразу дает читателю представление о его цель.

И по определению, если формат данных изменится, так будут и классы, которые работают на нем, поэтому вам все равно придется их менять.

Ответ 3

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

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

Ответ 4

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

Не слишком зависеть от того, придерживаетесь ли вы исключительно одной методологии программирования или находите решение, которое соответствует вашей проблеме. Я думаю, что существуют вполне допустимые контексты для одиночных игр (например, журнал).

Ответ 5

Я использую комбинацию наличия одного глобального объекта и передачи интерфейсов через конструкторы.

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

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

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

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

Ответ 6

Я предпочитаю использовать шаблон singleton, как описано в книге GoF для этих ситуаций. Синглтон не совпадает с одним из трех вариантов, описанных в вопросе. Конструктор является закрытым (или защищенным), поэтому его нельзя использовать нигде. Для получения экземпляра вы используете функцию get() (или все, что вы хотите назвать). Однако архитектура одноэлементного класса гарантирует, что каждый вызов метода get() возвращает один и тот же экземпляр.

Ответ 7

Мы должны позаботиться о том, чтобы не путать объектно-ориентированный дизайн с объектно-ориентированной реализацией. Слишком часто термин OO Design используется для оценки реализации, так же как и imho, здесь.

Дизайн

Если в вашем дизайне вы видите много объектов, имеющих ссылку на один и тот же объект, это означает много стрелок. Дизайнер должен чувствовать зуд здесь. Он должен убедиться, что этот объект обычно используется, или если он действительно полезен (например, COM factory, какой-то реестр,...).

Из требований проекта он может видеть, действительно ли он должен быть одноэлементным (например, "Интернет" ), или если объект разделяется, потому что он слишком общий или слишком дорогой или вообще.

Реализация

Когда вас попросят внедрить OO Design на языке OO, вы сталкиваетесь с множеством решений, например, как вы упомянули: как я должен реализовать все стрелки для часто используемого объекта в проекте?

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

Этап проектирования должен был уточнить, должен ли объект быть одиночным или нет. На этапе реализации будет определено, как эта однозначность будет представлена ​​в программе.

Ответ 8

Вариант 3), а не пурист OO, как правило, является наиболее разумным решением. Но я бы не сделал свой класс синглом; и использовать какой-то другой объект в качестве статического словаря для управления этими разделяемыми ресурсами.

Ответ 9

Мне не понравилось ни одно из предлагаемых вами решений:

  • Вы передаете кучу "контекстных" объектов - все, что их использует, не указывает, какие поля или части данных они действительно интересуют.
  • См. здесь описание шаблона God Object. Это самый худший из всех миров.
  • Просто не используйте объекты Singleton для чего-либо. Кажется, вы сами идентифицировали некоторые потенциальные проблемы.