Что если глобальные значения имеют смысл?


10

У меня есть ценность, которая нужна многим объектам. Например, финансовое приложение с различными инвестициями в качестве объектов, и большинству из них нужна текущая процентная ставка.

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

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


2
Является ли процентная ставка фиксированной на время ваших расчетов, или вы делаете что-то вроде симуляции, где она может изменяться между временными шагами?
Джеймс

Это больше похоже на симуляцию - она ​​может измениться во время бега.
Грег

В таком случае действительно ли каждая инвестиция должна сохранять процентную ставку или она может получать ее через параметр updateфункции, которая вызывается на каждом временном шаге? Можете ли вы написать в псевдокоде, как работает ваша симуляция?
Джеймс

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

Процентная ставка похожа на временной ряд, который представляет собой функцию, которая принимает в DateTimeкачестве входных данных и возвращает число в качестве выходных данных.
Рулон

Ответы:


14

У меня есть ценность, которая нужна многим объектам.

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

Итак, как я могу поделиться значениями между многими объектами, не перегружая мой дизайн?

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


17
Проблема с исключительными обстоятельствами заключается в том, что в программном обеспечении реального мира они не настолько исключительны.
Mattnz

Я думаю, что это серьезное недоразумение. Наши алгоритмы существуют в разных контекстах одновременно. Первый - это глобальный контекст. Тогда «этот» контекст для объектно-ориентированных языков. Контекст сеанса в веб-сервисе, контекст транзакции в среде БД, главное окно для графического интерфейса, ... и есть фрагменты информации, которые относятся к этим контекстам. Они должны «торчать» и быть доступными, одинаковыми наборами (объектами) для любого в том же контексте. Это нормально. Проблема заключается в решении этого для каждого объекта, а не путем создания службы контекста или использования инфраструктуры, такой как Spring в Java.
Лоран Кедвес

3
В текущей процентной ставке нет ничего исключительного. Существует много примеров того, как в реальном мире предмет имеет одно значение: ограничение скорости на открытой дороге, допустимый уровень содержания алкоголя в крови, ставка налога на добавленную стоимость (или НДС) и многие другие. В этом разница между наукой и техникой - инженеры решают сегодняшние проблемы реального мира, ученые мечтают о том дне, когда реальный мир уместится в красивые коробки совершенства и решит эти проблемы.
Mattnz

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

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

10

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

Например:

  • Сервисный уровень (библиотека классов) - создание экземпляра объекта FinancialEnvironment с помощью синглтона
  • Уровень бизнес-логики (библиотека классов) - принимает объект FinancialEnvironment от уровня службы
  • Уровень доступа к данным (библиотека классов) - принимает объект FinancialEnvironment от уровня обслуживания (или, в зависимости от вашей архитектуры, уровня бизнес-логики). Или, может быть, Singleton вызывает Уровень доступа к данным, чтобы получить информацию, такую ​​как процентная ставка, из хранилища (база данных / веб-служба / служба WCF) в любом случае.
  • Библиотека объектов (или DTO, если вы хотите это так называть) - где находится объект FinancialEnvironment. Все остальные библиотеки классов имеют ссылку на библиотеку классов Entities.

Другие классы связаны только через библиотеку классов Entities, они принимают экземпляр объекта FinancialEnvironment. Они не заботятся о том, как он был создан, только сервисный уровень, все, что им нужно, это информация. Синглтон также может быть достаточно умен для хранения нескольких объектов FinancialEnvironment, в зависимости от локальных правил, как указал @Telastyn.

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

Обновить:

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

Если бы я был букмекером, я бы поспорил, что процентная ставка была сохранена где-то в базе данных или была получена через веб-сервис. В этом случае для получения этой информации будет рекомендовано хранилище (уровень доступа к данным). Чтобы избежать ненужных посещений базы данных (я не уверен, как часто меняются процентные ставки или другая информация в классе FinancialInformation), можно использовать кэширование. В мире C # библиотека Microsoft Caching Application Block работает очень хорошо.

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


8
Тьфу. Создание глобального синглтона не делает его менее вонючим. Во всяком случае, вы ограничивали себя намного больше.
Теластин

3
@DavidCowden не имеет значения, если они не сложны для реализации; они делают ваш дизайн хуже, чем глобальные. Они глобальны и навязывают (ненужные) ограничения, которые есть только у вас.
Теластин

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

4
Singletonявляется глобальным с синтаксическим сахаром OO и опорой для ленивых и слабоумных. Singleton/globalэто самый худший способ тесно связать ваш код с чем-то, что потом станет раком, когда вы поймете, какая это колоссально плохая идея и почему все так говорят!

4
@Telastyn: К сожалению, реальность заключается в том, что самые совершенные проекты, когда они покидают совершенно упорядоченный мир теоретического проектирования программного обеспечения и присоединяются к реальному миру, получают fubar'd.
Mattnz

4

Файлы конфигурации?

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


Таким образом, пользователь будет обновлять файл конфигурации каждый раз, когда изменяется процентная ставка?
Калеб

2
Почему бы нет? зависит от «переменной», конечно, вещи, которые часто меняются, должны быть помещены в CENTRALIZED хранилище данных.
Темная ночь

1

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

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

Мое первоначальное предложение состоит в том, что если у вас есть несколько разных классов, которым нужна текущая процентная ставка, то вы, вероятно, захотите, чтобы они реализовали IInterestRateConsumerили что-то в этом роде. Внутри этого интерфейса вы будете иметь SetCurrentInterestRate(double rate)(или то, что имеет смысл), или, может быть, просто свойство.

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


Прохождение процентной ставки - это связь, это не плохая связь.
vaughandroid

1

У Мартина Фаулера есть статья, в которой кратко рассказывается о том, как преобразовать статический глобал в нечто более гибкое. По сути, вы превращаете его в одноэлементный, затем изменяете одноэлементный, чтобы он поддерживал переопределение класса экземпляра с помощью подкласса (и при необходимости перемещаете логику, которая создает экземпляр, в отдельный класс, который можно разделить на подклассы, что вы должны сделать если при создании экземпляра суперкласса возникает проблема с его последующей заменой).

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

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


0

Почему бы не поместить данные о процентной ставке в центральный кеш?

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

Или перейдите к целиком и сохраните их в базе данных, которая позволит вам масштабироваться до нескольких серверов.


0

В таких ситуациях я успешно ввел (повторно использовал) термин «контекст», иногда с несколькими слоями.

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

Магазин может быть:

  • строго типизированный: вы включаете заголовки для всех обслуживаемых типов и, таким образом, можете создавать типизированные методы доступа, такие как InterestRate getCurrentInterestRate ();
  • или универсальный: объект getObject (enum obType); и только расширяйте перечисление obType новыми видами (obtypeCurrentInterestRate).

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

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

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

Когда приходит запрос, вы решаете, на каком уровне контекста находится запрошенный объект, получите этот контекст для вызова. Если объект есть, вы отправляете его обратно; если нет, вы создаете и сохраняете его на этом уровне контекста и возвращаете его. Конечно, синхронизировать раздел создания (и опубликовать его в распределенном кеше). Создание может быть настраиваемым, как плагин, лучше всего с языками, позволяющими создавать экземпляры объектов по имени их класса (Java, Objective C, ...), но вы можете сделать это и в C с подключаемыми библиотеками, имеющими фабричные функции.

Примечание: вызывающий объект НЕ должен знать слишком много о своих собственных контекстах и ​​уровне контекста запрашиваемого объекта. Причины: 1: легко допустить ошибку (или «умные трюки»), играя с этими параметрами; 2: уровень контекста запрошенного может измениться позже. Я в основном подключаю контекстную информацию к потоку, поэтому хранилище объектов содержит информацию без дополнительных параметров из запроса.

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

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

Еще одна хорошая новость: если вы находитесь на Java, вы получаете этот сервис от Spring, не задумываясь, я просто хотел объяснить это подробнее.


0

Причина, по которой НЕ используется глобальный (или синглтонный) код, заключается в том, что даже если изначально вы ожидаете иметь только одно значение, часто удивительно полезно иметь возможность использовать один и тот же код несколько раз в одной и той же программе, например:

  • вычислить, что случилось бы, если бы процентная ставка была другой
  • некоторые компоненты зависят от процентной ставки в США, а некоторые компоненты зависят от процентной ставки в Великобритании

Я бы сделал процентную ставку членом класса «финансовых инструментов» и согласился бы с тем, что вам необходимо передать ее любым классам участников (либо на расчёт, либо на указатель / привязку к нему при создании).


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.