Дизайн: Метод об'єкта проти методу окремого класу, який приймає Object як параметр?


14

Наприклад, чи краще це зробити:

Pdf pdf = new Pdf();
pdf.Print();

або:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

Ще один приклад:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

або:

Country m = new Country("Mexico");
Country us = new Country("US");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(us);
double mRatio = ds.GetDebtToGDPRatio(m);    

Моє занепокоєння в останньому прикладі полягає в тому, що існує потенційно нескінченна статистика (але скажімо, навіть, лише 10), яку ви можете знати про країну; чи всі вони належать на дачному об'єкті?

напр

Country m = new Country("Mexico");
double ratio = m.GetGDPToMedianIncomeRatio();

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

Де цей рядок між операціями, які є властивими об'єкту проти операцій, які можна виконувати на об'єкті, але не є їх частиною?

Відповіді:


16

Беручи приклади PDF як вихідну точку, давайте розглянемо це.

http://en.wikipedia.org/wiki/Single_responsibility_principle

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

http://en.wikipedia.org/wiki/Separation_of_concerns

Принцип розділеності питань стосується того, що класи не повинні мати функцій, що перетинаються.

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

Тепер у вашому прикладі PDF питання, хто відповідає за друк? Що має сенс?

Перший фрагмент коду:

Pdf pdf = new Pdf();
pdf.Print();

Це не добре. Документ PDF не друкується сам. Він друкується ... ta da! .. принтером. Отже, ваш другий фрагмент коду набагато краще:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

Це має сенс. Принтер Pdf друкує документ PDF. А ще краще - принтер не повинен бути принтером PDF або фотопринтером. Це повинен бути просто принтер, здатний надрукувати надсилані до нього речі, якнайкраще.

Pdf pdf = new Pdf();
Printer printer = new Printer();
printer.Print(pdf);

Так що це просто. Покладіть методи там, де вони мають сенс. Очевидно, це не завжди так просто. Візьмемо, наприклад, статистику вашої країни:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

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

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

І в цьому криється річ: які ваші вимоги? Ваші вимоги визначатимуть те, як ви моделюєте світ, контекст, у якому ці вимоги мають бути задоволені.

Якщо у вас дійсно є багато / змінна кількість статистичних даних, то ваш другий приклад має більше сенсу:

Country m = new Country("Mexico");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(m);

А ще краще мати абстрактний суперклас або інтерфейс під назвою Статистика, який приймає країну як параметр:

interface StatisticsCalculator // or a pure abstract class if doing C++
{
   double getStatistics(Country country); // or a pure virtual function if in C++
}

клас DebtToGDPRatioStatisticsCalculator реалізує StatisticsCalculator ....

клас InfantMortalityStatisticsCalculator реалізує StatisticsCalculator ...

І так далі, і так далі. Що призводить до наступного: узагальнення, делегування, абстрагування. Статистичний збір делегується конкретним випадкам, які узагальнюють конкретну абстракцію (API збору статистики).

Я не знаю, чи відповідає це на ваше запитання 100%. Зрештою, у нас немає непогрішних моделей, заснованих на недоторканних законах (як це роблять люди з ЕЕ). Все, що ви можете зробити, - це зробити те, що вони мають сенс. І це інженерне рішення, яке потрібно прийняти. Найкраще зробити це - по-справжньому ознайомитись з принципами ОО (та принципами хорошого моделювання програмного забезпечення взагалі.)


1
+1 для інтерфейсу StatisticsCalculator (та подальше використання шаблону стратегії). І ґрунтовна продумана відповідь
edwardsmatt

3
На сьогоднішній день недостатньо часу для того, щоб це деконструювати, але треба зазначити, що клас Принтер з часом стане класом Бога, щільно поєднаним з усіма класами документів. Pdf.Print був би кращим - але все залежить від того, як ви визначаєте "єдину відповідальність" ;-)
Стівен А. Лоу

@Steve - те, що ви пропонуєте, - жахлива ідея (мати Pdf реалізацію print ()). Це не відображає те, як друк реалізований у реальному житті. Кожна операційна система та API друку, які я знаю, містять абстракцію Printer. Перегляньте список принтерів на вашій машині XP / Vista (або під / var / spool або еквівалент у * nix.) Кожна програма додатково серіалізує об’єкт документа на одному зі своїх принтерів. Немає ні принтера Word, ні текстового принтера, ні принтера PDF. Існують лише принтери, характерні для друкарського пристрою та не характерні для типу документа.
luis.espinal

2
+1 Мені це подобається, я розмірковую над тим, що ви сказали. це, мабуть, і розумно), і переклад певного типу документа (скажімо, у текстовому документі ms) повинен бути обов'язком якогось третього класу в один із цих стандартних форматів.
Користувач

2
Мені здається, що, можливо, PDF повинен мати можливість відображатись або на інтерфейсі Canvas, або в об'єкт Image, який потім може бути оброблений об'єктом Printer.
Вінстон Еверт

4

Я думаю, що жодне, безумовно, не краще, ніж інше. Використання pdf.Print () є більш жорстким, але мати клас PdfPrinter може бути краще, якщо:

  • Вам потрібно керувати примірниками принтерів
  • Існує широкий спектр варіантів та дій, які б підірвали складність pdf.Print (...) (наприклад, скасування друку, додаткове форматування тощо)

Я б інакше не зациклювався на цьому.


хороша, практична відповідь; час покаже, як це має розвиватися
Стівен А. Лоу

1
Коротка пропозиція полягає в тому, щоб при застосуванні SRP переглянути як логіку, так і дані , щоб вирішити, чи пошкодуємо ми не розв’язати їх раніше. Проблема зі збереженням параметрів принтера в Pdfкласі полягає в тому, що вони не повинні зберігатися разом - Pdfвони зберігаються у файлі, але параметри принтера повинні зберігатися з профілем користувача / машини.
rwong
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.