Уникнення роздутих об'єктів домену


12

Ми намагаємось перенести дані з нашого розширеного сервісного рівня в наш доменний рівень, використовуючи підхід DDD. Наразі у наших послугах є велика ділова логіка, яка розповсюджується всюди і не отримує користі від успадкування.

У нас є центральний клас доменів, який є основною частиною нашої роботи - торгівля. Об'єкт Торгівлі буде знати, як собі цінувати, як оцінювати ризик, перевіряти себе тощо. Потім ми можемо замінити умови на поліморфізм. Напр .: SimpleTrade буде собі ціну в одну сторону, але ComplexTrade буде собі ціну іншим.

Однак ми стурбовані тим, що це пошкодить клас (и) торгівлі. Він дійсно повинен відповідати за власну обробку, але розмір класу збільшиться експоненціально, оскільки додаватимуться більше функцій.

Тож у нас є вибір:

  1. Помістіть логіку обробки в торговий клас. Логіка обробки тепер поліморфна на основі типу торгівлі, але тепер у класу Trade є багато відповідальних (ціноутворення, ризик тощо) і є великим
  2. Покладіть логіку обробки в інший клас, наприклад TradePricingService. Більше не поліморфно з деревом спадкування Trade, але класи менші та простіші для перевірки.

Який би був запропонований підхід?


Без проблем - я радий прийняти міграцію!

1
Остерігайтеся зворотного: martinfowler.com/bliki/AnemicDomainModel.html
TrueWill

1
"розмір класу збільшиться експоненціально, оскільки додається більше функцій" - будь-який програміст повинен знати краще, ніж неправильно використовувати таке слово "експоненціально".
Майкл Боргвардт

@Piskvor, що це просто
дурно

@Arnis L.: Дякую за Ваш продуманий коментар. Зауважте, що це було, цитуйте, "перенесено з stackoverflow.com 22 листопада о 22:19", а також звідти мій коментар. Зараз я видалив свій коментар, що "це було б краще у програмістів.SE"; Тепер, чи є щось додати, чи це була єдина ідея, яку ви хотіли висловити?
Пісквор вийшов з будівлі

Відповіді:


8

Якщо ви збираєтесь керувати доменом, розгляньте, як розглядати клас Trade як сукупний корінь і розподіліть його обов'язки на інші класи.

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

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


Я згоден. Мислення об'єктів з точки зору поведінки, яка формує рішення (Ціноутворення, оцінка ризику), а не намагання моделювати проблему, дозволяє уникнути цих монолітних класів.
Гарретт Холл

Також згоден. Склад, багато менших класів із специфічними, єдиними обов'язками, обмежене використання приватних методів, багато щасливих інтерфейсів тощо
Ian

4

Ваше запитання, безумовно, змушує мене думати про стратегію . Тоді ви можете обмінятися різними стратегіями торгівлі / ціноутворення, подібними до тих, що вам дзвонять TradePricingService.

Я напевно думаю, що порада, яку ви отримаєте тут, - використовувати склад замість спадщини.


2

Одне можливе рішення, яке я використав у подібному випадку, - це схема дизайну адаптера (на вказаній сторінці міститься багато зразкового коду). Можливо, у поєднанні із схемою дизайну делегації для легкого доступу до основних методів.

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

interface IPriceCalculator {
  double getPrice(ITrader t);
}
interface ITrader {
  IPriceCalculator getPriceCalculator();
}
class Tracer implements ITrader {
  private IPriceCalculator myPriceCalculator = null;
  IPriceCalculator getPriceCalculator() {
    if (myPriceCalculator == null)
      myPriceCalculator = PriceCalculatorFactory.get(this);
    return myPriceCalculator;
  }
}

Однією з головних переваг такого підходу є те, що можливі комбінації, наприклад, ціни та рик, повністю розділені, і, таким чином, можна комбінувати за потребою. Це досить складно з однонаточним успадкуванням більшості мов програмування. Рішення, яку комбінацію використовувати, можна навіть обчислити дуже пізно :-)

Зазвичай я намагаюся зберігати класи адаптерів - наприклад, підкласи IPriceCalculatorвище - без стану. Тобто ці класи не повинні містити локальних даних, якщо це можливо, щоб зменшити кількість екземплярів, які потрібно створити. Тому я зазвичай надаю основний адаптований об'єкт як аргумент у всіх методах - як у getPrice(ITrader)вище.


2

не можу сказати багато про ваш домен, але

У нас є центральний клас доменів, який є основною частиною нашої роботи - торгівля.

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

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

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

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