То, что система сложна, не означает, что вы должны усложнять ее . Если у вас есть класс, который имеет слишком много зависимостей (или коллабораторов), как это:
public class MyAwesomeClass {
public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
// Assign it all
}
}
... тогда это стало слишком сложно, и ты не следишь за SRP , не так ли? Могу поспорить, если бы вы записали, что MyAwesomeClassна карточке CRC она не помещается на карточку, или вы должны писать очень мелкими неразборчивыми буквами.
Здесь у вас есть то, что ваши ребята следовали только принципу разделения интерфейса и, возможно, довели его до крайности, но это совсем другая история. Можно утверждать, что зависимости являются объектами домена (что происходит), однако наличие класса, который одновременно обрабатывает 20 объектов домена, слишком растягивает его.
TDD даст вам хороший показатель того, сколько делает класс. Прямо поставил; если у тестового метода есть код установки, на запись которого уходит вечность (даже если вы реорганизуете тесты), то у вас, MyAwesomeClassвероятно, слишком много дел.
Итак, как вы решаете эту головоломку? Вы перемещаете обязанности в другие классы. Есть несколько шагов, которые вы можете предпринять для класса с такой проблемой:
- Определите все действия (или обязанности), которые ваш класс выполняет со своими зависимостями.
- Сгруппируйте действия в соответствии с тесно связанными зависимостями.
- Дать полномочия! Т.е. рефакторинг каждого из выявленных действий либо новым, либо (что более важно) другим классам.
Абстрактный пример рефакторинга ответственности
Пусть Cбудет класс , который имеет несколько зависимостей D1, D2, D3, D4что вам нужно реорганизовать , чтобы использовать меньше. Когда мы определяем, какие методы Cвызывают зависимости, мы можем составить простой список:
D1- performA(D2),performB()
D2 - performD(D1)
D3 - performE()
D4 - performF(D3)
Глядя на список, мы видим это D1и D2связаны друг с другом, так как класс так или иначе нуждается в них. Мы также можем видеть, что D4необходимо D3. Итак, у нас есть две группировки:
Group 1- D1<->D2
Group 2- D4->D3
Группировка является показателем того, что у класса теперь есть две обязанности.
Group 1- Один для обработки вызова двух объектов, которые нужны друг другу. Возможно, вы можете позволить своему классу Cустранить необходимость обработки обеих зависимостей и оставить одну из них для обработки этих вызовов. В этой группе очевидно, что D1может иметь ссылку на D2.
Group 2- Другая ответственность нуждается в одном объекте, чтобы вызвать другой. Не можете D4справиться D3вместо вашего класса? Тогда мы, вероятно, можем исключить D3из класса C, разрешив D4вместо этого делать вызовы.
Не принимайте мой ответ, изложенный в камне, так как пример очень абстрактный и содержит много предположений. Я почти уверен, что есть и другие способы реорганизовать это, но, по крайней мере, эти шаги могут помочь вам создать какой-то процесс перемещения обязанностей вместо разделения классов.
Редактировать:
Среди комментариев @ Эммад Карем говорит:
«Если ваш класс имеет 20 параметров в конструкторе, это не похоже на то, что ваша команда знает, что такое SRP. Если у вас есть класс, который выполняет только одну задачу, как у него 20 зависимостей?» - я думаю, что если вы есть класс Customer, это не странно иметь 20 параметров в конструкторе.
Это правда, что объекты DAO имеют тенденцию иметь много параметров, которые вы должны установить в своем конструкторе, и параметры, как правило, являются простыми типами, такими как string. Однако в примере с Customerклассом вы можете сгруппировать его свойства внутри других классов, чтобы упростить задачу. Например, наличие Addressкласса с улицами и Zipcodeкласса, который содержит почтовый индекс и будет обрабатывать бизнес-логику, такую как проверка данных:
public class Address {
private String street1;
//...
private Zipcode zipcode;
// easy to extend
public bool isValid() {
return zipcode.isValid();
}
}
public class Zipcode {
private string zipcode;
public bool isValid() {
// return regex match that zipcode contains numbers
}
}
Эта тема обсуждается далее в сообщении блога «Никогда, никогда, никогда не используйте String в Java (или, по крайней мере, часто)» . В качестве альтернативы использования конструкторов или статических методов для облегчения создания подобъектов вы можете использовать шаблон построителя флюидов .