Как управлять единой ответственностью, когда ответственность разделена?

10

У меня есть два базовых класса, Operationи Trigger. У каждого есть несколько подклассов, которые специализируются на определенных типах операций или триггеров. А 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и a Operation. Он будет использовать эту информацию для создания отчета.

Джеймс Вуд
источник
Почему тип bool?
Tulains Córdova
@ user61852, чтобы вернуть результат в код вызова
Джеймс Вуд
1
Что делает вызывающий код с логическим значением? В зависимости от того, что вы ответите на этот вопрос, у меня может быть решение.
Тулаинс Кордова
@ user61852, пожалуйста, смотрите мои правки.
Джеймс Вуд
1
Так что это не имеет ничего общего с фактическим выполнением триггера операции?
Тулаинс Кордова

Ответы:

4

Я думаю об этом так: как определяется, какая операция вызывает какой триггер запускается. Это должен быть алгоритм, который может изменяться со временем или развиваться в несколько алгоритмов. Включение его в классы Trigger или Operation означает, что эти классы смогут обрабатывать такие сценарии в будущем. Обратите внимание, что я не рассматриваю это так просто, как отображение, так как может быть что-то еще.

Мой выбор - создать класс с соответствующими методами, такими как GetOperationForTrigger (Trigger t). Это позволяет коду превращаться в набор таких классов, выбор которых может зависеть во время выполнения или других переменных (например, шаблон стратегии).

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

Надеюсь это поможет. Хотя ответ похож на user61852, рассуждения разные. В результате реализация будет отличаться (т. Е. Иметь явные методы вместо переопределения equals, поэтому число методов может развиваться с течением времени в зависимости от потребностей).

Омер Икбал
источник
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. Это очень сложная связь.

По определению A выполняет, по крайней мере, свою собственную операцию И проверяет, должен ли B выполняться. Обратное верно в отношении B. Какой бы класс ни назывался первым, он должен иметь возможность посмотреть на результат и посмотреть, нужно ли ему что-то еще делать.

Попробуйте разорвать эту связь, разбив ваши классы на более мелкие компоненты. Мне нравится помещать комментарий в начале каждого класса, объясняя простым языком, что он делает. Если вам нужно использовать такие слова, как «И», или если они идут в одном или двух предложениях, вам следует подумать о том, чтобы разбить их. Как правило, все, что после «и» должно быть в своем собственном классе

Также посмотрите, можете ли вы определить интерфейс, который охватывает функциональные возможности триггера и операции. Если вы не можете, это еще один признак того, что ваш класс становится слишком большим. Это также нарушит связь между вашими классами.

Том Сквайрс
источник
1
Честно говоря, я не уверен, что смогу сломать свой код дальше. Я обновил свой вопрос с помощью сигнатур классов, чтобы вы могли видеть, но в основном это довольно легкие объекты данных, каждый из которых содержит несколько свойств. С точки зрения связи, да, это немного проблематично, так как эффективно a Triggerбудет связано с a Operation. Но именно так выглядят данные реального мира. Они связаны, потому что есть отображение, например, они должны знать друг о друге, чтобы иметь смысл.
Джеймс Вуд