Работа с «глобальными» структурами данных в объектно-ориентированном мире - PullRequest
15 голосов
/ 01 октября 2008

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

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

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

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

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

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

Ответы [ 9 ]

9 голосов
/ 01 октября 2008

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

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

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

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

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

4 голосов
/ 01 октября 2008

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

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

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

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

2 голосов
/ 01 октября 2008

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

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

1 голос
/ 18 ноября 2008

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

Дизайн

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

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

Осуществление

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

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

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

1 голос
/ 02 октября 2008

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

1 голос
/ 01 октября 2008

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

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

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

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

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

1 голос
/ 01 октября 2008

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

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

0 голосов
/ 01 октября 2008

Вариант 3), хотя не является пуристическим ОО, имеет тенденцию быть наиболее разумным решением. Но я бы не стал делать твой класс синглтоном; и использовать некоторый другой объект в качестве статического «словаря» для управления этими общими ресурсами.

0 голосов
/ 01 октября 2008

Мне не нравится ни одно из предложенных вами решений:

  1. Вы передаете кучу «контекстных» объектов - вещи, которые их используют, не указывают, какие поля или части данных им действительно интересны
  2. Смотрите здесь описание объекта Бога . Это худший из всех миров
  3. Просто никогда не используйте объекты Singleton ни для чего. Кажется, вы сами определили несколько потенциальных проблем
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...