Розповсюдження інформації через межі об'єктів


10

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

Як приклад, припустимо, що у нас є Порядок, який має ряд OrderItems, який посилається на InventoryItem, який має ціну. Я закликаю Order.GetTotal (), який підсумовує результат OrderItem.GetPrice (), який множить кількість на InventoryItem.GetPrice (). Все йде нормально.

Але потім ми з'ясовуємо, що деякі предмети продаються разом з двома за одну угоду. Ми можемо впоратися з цим, змусивши OrderItem.GetPrice () зробити щось на кшталт InventoryItem.GetPrice (кількість) і дозволити InventoryItem вирішувати це.

Однак тоді ми з'ясовуємо, що угода "два на один" триває лише певний часовий період. Цей часовий період повинен базуватися на даті замовлення. Тепер ми змінюємо OrderItem.GetPrice () на InventoryItem.GetPrice (quatity, order.GetDate ())

Але тоді нам потрібно підтримувати різні ціни залежно від того, як довго клієнт перебуває в системі: InventoryItem.GetPrice (кількість, замовлення.GetDate (), order.GetCustomer ())

Але потім виявляється, що угоди "два на один" застосовуються не лише до купівлі кількох одного і того ж предмета інвентарю, але до кількох для будь-якого предмета в InventoryCategory. У цей момент ми кидаємо руки і просто надаємо InventoryItem елемент замовлення і дозволяємо йому пересуватися по об'єктному довідковому графіку через аксесуари, щоб отримати необхідну інформацію: InventoryItem.GetPrice (це)

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

Чи є хороші методи боротьби з цим? Чи виявляють інші цю ж проблему?


4
На перший погляд здається, що клас InventoryItem намагається зробити занадто багато, обчисливши ціну, повернути задану ціну - це одне, але ціни продажу та спеціальні правила бізнесу не повинні бути адресами в InventoryItem. Розгорніть клас для розрахунку ваших цін і нехай він обробляє потребу в даних із замовлення, товарних запасів та клієнтів тощо.
Кріс

@Chris, так, розбиття логіки ціноутворення, ймовірно, гарна ідея. Моя проблема полягає в тому, що такий об’єкт буде щільно з'єднаний з усіма об'єктами, що стосуються порядку. Ось та частина, яка мене турбує, і частина, яку мені цікаво, чи зможу я уникнути.
Вінстон Еверт

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

Відповіді:


6

У нас в основному той самий досвід, де я працюю, і ми вирішили це, маючи клас OrderBusinessLogic. Здебільшого макет, який ви описали, працює для більшості нашого бізнесу. Це приємно, чисто і просто. Але в тих випадках, коли ви купуєте будь-які 2 з цієї категорії, ми розглядаємо це як "ділове вилучення", і клас OrderBL перераховує підсумки, перебираючи потрібні об'єкти.

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


2
Якщо ви сумніваєтесь, додайте ще один клас.
Кріс

1

Схоже , вам потрібен окремий об'єкт зі знижкою (або їх список) , який відстежує всі , що матеріал, а потім застосувати Дисконт (и) до ордену, що - щось на зразок Order.getTotal(Discount)або Discount.applyTo(Order)чи подібні.


Я бачу, де краще розмістити код у знижці, а не в об’єкті Інвентар. Але, здається, об’єкт знижок все-таки повинен мати доступ до інформації з усіх різних об'єктів, щоб виконати свою роботу. Тож це, принаймні, наскільки я бачу, не вирішує проблему.
Вінстон Еверт

2
Я вважаю, що об'єкт "Знижка" - це гарна ідея, але я би змусив його реалізувати схему спостерігачів ( en.wikipedia.org/wiki/Observer_pattern ), зі знижками як предмет (и) шаблону
BlackICE

1

Цілком нормально отримати доступ до даних з інших класів. Однак ви хочете, щоб це були односпрямовані відносини. Наприклад, припустимо, що ClassOrder звертається до CallItem. В ідеалі ClassItem не повинен мати доступ до ClassOrder. Те, що я думаю, що у вашому класі замовлення відсутнє, - це якась бізнес-логіка, яка може або не вимагає такого класу, як пропонував Вальтер.

Редагувати: Відповідь на коментар Вінстона

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

Я б посилався на інвентарні товари за ідентифікатором. Кожне замовлення міститиме перелік ідентифікаційних запасів і відповідну кількість.

Тоді я б обчислив загальну суму замовлення приблизно таким чином.
Inventory.GetCost (Пункти, Ім'я клієнта, дата)

Тоді у вас може бути інша помічна функція, наприклад:

Inventory.ItemsLefts (int itemID)
Inventory.AddItem (int itemID, int количество)
Inventory.RemoveItem (int itemID, int количество)
Inventory.AddBusinessRule (...)
Inventory.DeleteBusinessRule (...)


Цілком чудово запитувати дані з тісно пов'язаних класів. Проблема виникає, коли я отримую доступ до даних із непрямо пов'язаних класів. Для реалізації цієї логіки в класі Order вимагатиме поводження класу Order безпосередньо з об'єктом «Інвентар» (який містить необхідні дані про ціноутворення). Це та частина, яка мене турбує, оскільки вона прив'язує Орден до класів, про які (в ідеалі) він би нічого не знав.
Вінстон Еверт

По суті, Вашою ідеєю було б усунути OrderItem, а не Order відстежувати всю цю інформацію. Тоді легко передати цю інформацію іншому об'єкту для цінової інформації. Це могло б спрацювати. (У конкретному сценарії цей приклад вільно базується на OrderItem був надто складним і він справді повинен був бути його власним об'єктом)
Winston Ewert

Що для мене не має сенсу, то чому ви хочете, щоб Інвентар посилався на ідентифікаційні номери, а не на посилання на об'єкти. Мені здається способом краще відстежувати посилання на об'єкти та величини, а потім ідентифікатори та посилання на елементи.
Вінстон Еверт

Я зовсім не пропоную цього. Я думаю, що ви все-таки повинні мати клас або структуру для предметів замовлення. Я просто не думаю, що окремі об’єкти товарно-матеріальних цінностей повинні мати ціну всередині, головним чином, тому що я не впевнений, як би ви потрапили туди, запитуючи якусь базу даних. Порядок або товарний запас суворо буде класом даних, що містить ідентифікатор елемента та кількість. У самому наказі був би список цих об’єктів.
Pemdas

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

0

Поміркувавши над цим більше, я придумав власну альтернативну стратегію.

Визначте ціновий клас. Inventory.GetPrice()повертає ціновий об’єкт

Price OrderItem::GetPrice()
{
    return inventory.GetPrice().quantity(quantity);
}

Currency OrderItem::GetTotal()
{
    Price price = empty_price();
    for(item in order_items)
    {
         price = price.combine( item.GetPrice() )
    }
    price = price.date(date).customer(customer);
    return price.GetActualPrice();
}

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

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