Як керувати єдиною відповідальністю, коли відповідальність поділяється?


10

У мене базові два класи, Operationі Trigger. У кожному є ряд підкласів, які спеціалізуються на певних типах операцій або тригерів. A Triggerможе викликати конкретний Operation. У той час як Operationможе бути ініційований певним Trigger.

Мені потрібно написати код, який відображає задану Operationданість Trigger(або навпаки), але я не знаю, куди її поставити.

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

Я бачу три варіанти, які б усі працювали. Хоча 1 і 2, здається, є лише вибором семантики, 3 представляє цілком інший підхід.

  1. На спусковий гачок, напр bool Triggers(Operation o).
  2. Про операцію, наприклад bool TriggeredBy(Trigger t).
  3. У абсолютно новому класі, який управляє картографуванням, наприклад bool MappingExists(Trigger t, Operation o).

Як я повинен вирішити, де розмістити загальний код картографії стосовно єдиного принципу відповідальності?

Як керувати єдиною відповідальністю, коли відповідальність поділяється?


Редагуйте 1.

Тож власне код виглядає приблизно так. Всі властивості, є або string, Guid, collection<string>або enum. Вони в основному представляють лише невеликі фрагменти даних.

введіть тут опис зображення

Редагувати 2.

Причина зворотного типу bool. Інший клас буде споживати колекцію Triggerта колекцію Operation. Він повинен знати, де існує відображення між a Triggerі an Operation. Він використовуватиме цю інформацію для створення звіту.


Чому тип bool?
Тулан Кордова

@ user61852, щоб повернути результат на код за викликом
James Wood

1
Що робить код виклику з булевим? Залежно від того, що ви відповісте на це питання, я можу знайти рішення.
Тулан Кордова

@ user61852, будь ласка, дивіться мої зміни.
Джеймс Вуд

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

Відповіді:


4

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

Мій вибір буде створити клас відповідними методами, такими як GetOperationForTrigger (Trigger t). Це дозволяє коду перетворюватися на набір таких класів, вибір яких може залежати під час виконання або інших змінних (наприклад, шаблон стратегії).

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

Сподіваюсь, це допомагає. Хоча відповідь схожа на user61852, міркування різні. В результаті реалізація буде відрізнятися (тобто матимуть явні методи замість переважних рівних, тому кількість методів може змінюватися з часом залежно від потреб).


5

Був там зробив те.

Варіант №3.

Я не знаю, якою мовою ви будете користуватися, але я буду використовувати псевдо-код, який дуже схожий на Java. Якщо ваша мова є C #, ви, мабуть, маєте подібні інтерфейси та структури.

Майте клас Mapping або інтерфейс:

public interface Mapping {
    public void setObject1(Object o);
    public void setObject2(Object o);
    public Object getObjecto1();
    public Object getObjecto2();
}
  • Перевизначте equals()метод Mappingколекцій, тому Mappingможна запитати, чи вони містять задане відображення.
  • Еспеціалізовані об'єкти також повинні мати equals()методи присвоєння .
  • Також реалізуйте інтерфейс Comparable, щоб ви могли сортувати звіти.

Їх ви можете просто помістити зібрання у колекцію

List<Mapping> list = new ArrayList<Mapping>();
Hat hat = new Hat();
Bag bag = new Bag();
list.add(new Mapping(hat,bag));

Пізніше ви можете запитати:

// let's say you have a variable named x which is of type Mapping

if ( list.contains(x) ){
    // do some thing
}

0
  1. Розбийте свій код на менші шматочки.

В даний час у вас є клас A, який знає про клас B і клас B, знаючи про клас А. Це багато зчеплення.

За визначенням A робить принаймні власну операцію І перевіряє, чи слід запускати B. Зворотний бік справедливий для В. Залежно від того, хто називав клас, він повинен мати можливість подивитися на результат і побачити, чи потрібно його далі виконувати.

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

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


1
Чесно кажучи, я не впевнений, що можу далі зламати код. Я оновив своє запитання підписами класу, щоб ви могли бачити, але в основному це досить легкі об'єкти даних, що зберігають по кілька властивостей кожен. З точки зору зв'язку, так, це дещо проблематично, так як ефективно a Triggerбуло б поєднане з a Operation. Але це так, як виглядають дані реального світу. Вони поєднуються, тому що є картографування, наприклад, вони повинні знати один про одного, щоб мати значення.
Джеймс Вуд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.