Що робити, якщо глобалісти мають сенс?


10

У мене є значення, яке потрібно багатьом об’єктам. Наприклад, фінансовий додаток з різними інвестиціями як об'єкти, і більшості з них потрібна поточна процентна ставка.

Я сподівався закріпити своє "фінансове середовище" як об'єкт із відсотковою ставкою як власністю. Але об’єкти, які потребують цього значення, не можуть дістатися до нього.

Тож як я можу поділити значення між багатьма об'єктами, не переплутавши дизайн? Очевидно, я думаю про це неправильно.


2
Чи встановлена ​​процентна ставка на час ваших розрахунків чи ви робите щось на зразок симуляції, коли вона може змінюватись між часовими кроками?
Джеймс

Це більше схоже на моделювання - воно може змінюватися під час пробігу.
Грег

У такому випадку чи дійсно потрібно кожній інвестиції зберігати процентну ставку чи вона може отримувати її через параметр до updateфункції, яка викликається на кожному кроці часу? Чи можете ви розмістити в псевдокоді, як працює ваше моделювання?
Джеймс

3
Ви правильний трек, а Singletonце глобальний з синтаксичним цукром OO, і це жахливе рішення, яке щільно з'єднує ваш код якимись найгіршими можливими способами. Читайте цю статтю знову і знову, поки не зрозумієте її!

Процентна ставка - це як часовий ряд, який є функцією, яка приймає DateTimeвхід і повертає число як вихід.
rwong

Відповіді:


14

У мене є значення, яке потрібно багатьом об’єктам.

Це дизайнерський запах. Нечасто, що багато предметів повинні про щось знати. Однак, поточна процентна ставка є досить хорошим прикладом виняткових обставин. Варто турбуватися про те, що відсоткова ставка рідко буває . Різні фінансові інструменти використовують різні ставки. Принаймні, різні локалі використовують різні "стандартні" тарифи. Крім того, щоб допомогти в тестуванні та звітуванні, зазвичай ви хочете пройти курс, оскільки ви не хочете використовувати там поточну ставку. Ви хочете використовувати коефіцієнт "що робити" або "станом на дату звітності".

Тож як я можу поділити значення між багатьма об'єктами, не переплутавши дизайн?

Поділившись ними, не маючи їх усіх, вони посилаються на один екземпляр. Передача тієї ж речі все ще пов'язана до певної міри, але не надмірно пов'язана, оскільки щось необхідне як поточна процентна ставка потрібна як вхід для різних розрахунків.


17
Проблема виняткових обставин полягає в тому, що в реальному світі програмне забезпечення не є таким винятковим.
mattnz

Я думаю, це серйозне непорозуміння. Наші алгоритми існують одночасно в різних контекстах. Перший - глобальний контекст. Потім "цей" контекст для об'єктно-орієнтованих мов. Контекст сесії у веб-службі, контекст транзакцій у середовищі БД, Головне вікно для gui, ... і є фрагменти інформації, що належать до цих контекстів. Вони повинні "зависати" і бути доступними, однаковий набір (предмети) для всіх, хто в одному контексті. Це нормально. Проблема полягає у вирішенні цього завдання для кожного об'єкта, а не шляхом створення контекстної служби чи використання фреймворку, як Spring in Java.
Лоранд Кедвес

3
Немає нічого виняткового в поточній процентній ставці. Існує багато прикладів предметів реального світу, що мають одне значення, - обмеження швидкості на відкритій дорозі, прийнятний рівень алкоголю в крові, ставка податку GST (або ПДВ). У чому полягає різниця між наукою та технікою - інженери вирішують сучасні проблеми в реальному світі, вчені мріють про те, щоб день реального світу вписався в приємні коробки досконалості і вирішив ці проблеми.
mattnz

1
Я вибрав це як відповідь, тому що це просто і не покладається на десятиліття досвіду ООП. МНОГО дякую всім респондентам. Я мав цілий день читання завдяки безлічі довідок, але все ще залишаюся трохи здивований. На таке просте запитання я був здивований різноманітністю та емоціями позаду. Я все ще переконаний, що іноді існує центральне джерело глобальних, але різних даних, які найкраще обслуговуються Singleton. Я б не думав, що слід переходити вказівники вгору і вниз по ієрархії об'єктів, щоб уникнути Singleton. Ще раз дякую всім.
Грег

@mattnz, проблема полягає в тому, що кожен ваш приклад є змінним у випадках, коли ви поширюєте свою програму на кілька баз користувачів, які можуть охоплювати компанії, штати чи країни. Усі вони також можуть бути змінними в часі.
Ден Ліонс

10

У цьому конкретному випадку я б застосував шаблон Singleton . Фінансове оточення було б об'єктом, про який усвідомлюють інші бібліотеки класів, але він буде заснований на Singleton. Тоді в ідеалі ви б надіслали цей створений об'єкт різним бібліотекам класів.

Наприклад:

  • Службовий шар (бібліотека класів) - миттєво активує об'єкт FinancialEnvironment за допомогою однотонного
  • Шар бізнес-логіки (бібліотека класів) - приймає об’єкт FinancialEnvironment з рівня обслуговування
  • Рівень доступу до даних (бібліотека класів) - приймає об’єкт FinancialEnvironment з рівня обслуговування (або залежно від вашої архітектури Business Logic Layer). Або, можливо, Сінглтон використовує рівень доступу до даних, щоб отримати інформацію, таку як процентна ставка, із сховища (база даних / веб-сервіс / послуга WCF).
  • Суб'єкти (або DTO, якщо ви хочете назвати це так) бібліотека класів - де живе об'єкт FinancialEn Environment. Усі інші бібліотеки класів мають посилання на бібліотеку класів Entities.

Інші класи пов'язані лише за допомогою бібліотеки класів Entities, вони приймають об'єкт з об'єктом FinancialEn Environment. Їм не байдуже, як це було створено, лише сервісний рівень, все, що вони хочуть, - це інформація. Сингл також може бути досить розумним для зберігання декількох об'єктів FinancialEnvironment, залежно від правил для місцевих, як вказував @Telastyn.

Зі сторони, я не є шанувальником шаблону Singleton, я вважаю це кодовим запахом, оскільки його можна зловживати дуже легко. Але в деяких випадках це потрібно.

Оновлення:

Якщо ви абсолютно позитивно повинні мати глобальну змінну, то реалізація Шаблону Singleton, як описано вище, спрацює. Однак я не є великим прихильником цього, і на підставі коментарів з моєї оригінальної публікації ще кілька людей не є. Щось таке нестабільне, як процентний рейтинг, Singleton може виявитися не найкращим рішенням. Синглтони найкраще працюють, коли інформація не змінюється. Наприклад, я використовував Singleton в одному зі своїх програм, щоб створити лічильники ефективності. Тому що якщо вони все-таки змінюються, ви повинні мати логіку для обробки даних, що оновлюються.

Якби я був людиною, що робив ставки, я б ставлю ставку, що процентна ставка зберігалася десь у базі даних, або вона була отримана через веб-сервіс. У такому випадку рекомендується отримати сховище (рівень доступу до даних). Щоб уникнути зайвих поїздок до бази даних (я не впевнений, як часто змінюються процентні ставки, або інша інформація в класі фінансової інформації), кешування може бути використане. У світі C # бібліотека Microsoft Caching Application Block працює дуже добре.

Єдине, що змінилося б із наведеного вище прикладу, - це різні класи рівня обслуговування, які потребують, щоб фінансова інформація отримала з рівня доступу до даних замість того, щоб Singleton інстанціював об'єкт.


8
Тьфу. Зробити глобальний сингл не робить його менш смердючим. Якщо що-небудь ви обмежили себе тим більше.
Теластин

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

4
Я збирався опублікувати саркастичний коментар сказати "зроби це синглтон, і це перейде від поганої практики до найкращої практики", але потім я побачив, що це вже прийнята і проголосована відповідь. Дуже хороший!
Кевін

4
Singletonє глобальним з синтаксичним цукром OO та милицею для ледачих та слабко налаштованих. Singleton/globalце абсолютно найгірший спосіб щільно з'єднати свій код з чимось, що буде раком пізніше, коли ти зрозумієш, яка це була колосально погана ідея, і чому всі кажуть, що вони є!

4
@Telastyn: Прикрою реальністю є те, що найдосконаліші конструкції, коли вони залишають ідеально упорядкований світ теоретичного дизайну програмного забезпечення та приєднуються до реального світу, отримують фубард.
mattnz

4

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

Якщо у вас є значення, які використовуються "глобально", будь ласка, покладіть їх у файл конфігурації. Тоді кожна система та підсистема може посилатися на це та витягувати потрібні ключі, робити їх лише для читання.


Отже, ви повинні користувач оновлювати файл конфігурації щоразу, коли процентна ставка змінюється?
Калеб

2
Чому ні? Залежно від "змінної", звичайно, речі, які часто змінюються, слід розміщувати всередині централізованого сховища даних.
Темна ніч

1

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

Я можу вам сказати, що ви, мабуть, не дуже хочете глобального об’єкта. Запровадження такого роду грубощі надає набагато більше можливостей для зловживань, ніж це допомагає.

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

Передача процентної ставки навколо насправді не пов'язана - Якщо вашому класу потрібна процентна ставка, це частина його API. Це з'єднання лише в тому випадку, якщо один з ваших занять починає турбуватися про те, як саме інший клас використовує цю процентну ставку.


Передача процентної ставки навколо є зв'язком, це просто не погано .
vaughandroid

1

У Мартіна Фаулера є стаття, в якій коротко розповідається про те, як переробити статичний глобал у щось більш гнучко. В основному ви перетворюєте його в синглтон, потім змінюєте синглтон, щоб він підтримував переосмислення класу екземпляра з підкласом (і, якщо потрібно, перемістіть логіку, яка створює екземпляр, до окремого класу, який можна підкласифікувати, що ви б робили якщо створити екземпляр суперкласу, то замінити його пізніше - це проблема).

Звичайно, вам доведеться зважувати проблеми з однотонними (навіть замінними сингтонами) проти болю при передачі одного і того ж об'єкта скрізь.

Що стосується об'єкта "фінансового середовища" - це зручно програмувати з першого проходу, але коли ви закінчите, ви додали додаткові залежності. Класи, які просто потребують відсотковій ставці, тепер функціонують лише тоді, коли передано об'єкт фінансового середовища, що ускладнить їх повторне використання, якщо у вас не буде облягати об'єкт фінансового середовища. Тож я б заважав цьому широко передавати.


0

Чому б не помістити дані процентної ставки в центральний кеш?

Ви можете використовувати одну з декількох бібліотек кеш-пам'яті, залежно від ваших потреб, щось на зразок запам’ятовування вирішує всі проблеми, пов’язані з паралельним керуванням та керуванням кодом, і дозволить вашій програмі масштабуватись до кількох процесів.

Або перейдіть цілу свиню і зберігайте їх у базі даних, що дозволить вам масштабувати на декількох серверах.


0

У таких ситуаціях я успішно ввів (повторно) використав термін "контекст" з часом кількома шарами.

Це означає одиночний, таким чином, "глобальний" магазин об'єктів, з якого можна запитувати такі об'єкти. Коди, які вимагають їх, включають заголовок магазину та використовують глобальні функції, щоб отримати їх об'єкти (наприклад, зараз постачальник процентних ставок).

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

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

Чим більша система, тим більш зручним є останнє рішення, за досить малого ризику використання неправильної перерахунку. З іншого боку, з мовами, які дозволяють декларувати типи вперед, я думаю, що ви можете використовувати набрані аксесуари, не включаючи всі заголовки в магазин.

Ще одна примітка: у вас може бути кілька примірників одного і того ж типу об'єкта для різних цілей, наприклад, іноді різні значення мови для графічного інтерфейсу та для роздруківки, журнали глобального рівня та сеансу тощо, тому ім'я enum / accessor не повинно відображати фактичний тип , але роль запитуваного екземпляра (CurrentInterestRate).

У реалізації магазину ви повинні керувати рівнями контексту та колекціями екземплярів контексту. Простий приклад - веб-сервіс, де у вас глобальний контекст (один екземпляр для всіх запитів цього об’єкта - проблематичний, коли є ферма серверів) та контекст для кожного веб-сеансу. Ви також можете мати контексти для кожного користувача, який може мати кілька, паралельних сеансів і т. Д. На кількох серверах для таких речей ви повинні використовувати своєрідний розподілений кеш.

Коли запит надходить, ви вирішуєте, на якому рівні контексту запитуваний об’єкт, отримуєте цей контекст для виклику. Якщо об’єкт є, ви відправляєте його назад; якщо ні, ви створюєте та зберігаєте його на тому рівні контексту та повертаєте його. Звичайно, синхронізуйте розділ створення (і опублікуйте його в розподіленому кеші). Створення може бути налаштоване як плагін, найкраще з мовами, що дозволяють створювати екземпляри об'єктів за назвою їх класу (Java, Об'єктив C, ...), але ви можете це робити і в C, а також із підключаються бібліотеками, що мають фабричні функції.

Побічна примітка: абонент НЕ повинен знати занадто багато про власні контексти та рівень контексту запитуваного об'єкта. Причини: 1: легко помилитися (або "розумні хитрощі"), граючи з цими параметрами; 2: рівень контексту запитуваного може змінитися пізніше. Я здебільшого підключаю інформацію про контекст до потоку, тому в сховищі об’єктів є інформація без додаткових параметрів запиту.

З іншого боку, запит може містити натяк на приклад: наприклад, отримання процентної ставки на конкретну дату. Це повинен бути той самий "глобальний" доступ, але декілька примірників залежно від дати (і приведення різних значень дати до одного і того ж екземпляра між змінами ставок), тому доцільно до запиту додати об'єкт "підказки", який використовується примірник фабрика, а не магазин; і ключ для підказки до фабрики, яку використовує магазин. Ви можете додати ці функції пізніше, я щойно згадав.

Для вашого випадку це свого роду надмірність (лише один об’єкт подається на глобальному рівні), але для зовсім невеликого та простого додаткового коду зараз ви отримуєте механізм подальших, можливо, більш складних вимог.

Ще одна добра новина: якщо ви перебуваєте на Java, ви отримуєте цю послугу від Spring, не надто замислюючись, я просто хотів пояснити її детально.


0

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

  • підрахувати, що БУДЕ відбуватися, якщо процентна ставка була іншою
  • наявність деяких компонентів залежить від процентної ставки в США, а деякі компоненти залежать від процентної ставки Великобританії

Я ставлю ставку процентної ставки членом класу "фінансових інструментів" і погоджуюся з тим, що ви повинні передавати її будь-яким класам членів (або за розрахунок, або даючи їм вказівник / гачок на його побудову).


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.