Немного предыстории: я программист-самоучка, который начал работать на Python и изучал Java, когда я присоединился к MegaCorp (TM) 6 лет назад.Имея степень по математике, я довольно солиден (без каламбура) по алгоритмам и критическому мышлению, но я часто нахожусь с пробелом в знаниях, связанных со структурами данных, дизайном или другими основами CompSci, которые мои коллеги изучили в своей информатикекурсы.
С этой целью я попросил старшего инженера в моей команде рекомендовать книгу, чтобы помочь заполнить мои пробелы, и он предложил Чистая архитектура .
IЯ около трети пути, и действительно смущен одним из основных мотивирующих факторов предложений.Дядя Боб представляет многие идеи и принципы (в том числе принципы SOLID , о которых я слышал ранее, хотя я все еще сталкиваюсь с принципом подстановки Лискова) как предназначенные для "защиты" некоторыхчасть системы от требования к изменению.Есть несколько примеров этого, но самый ясный - на странице 73:
Если компонент A должен быть защищен от изменений в компоненте B, тогда компонент B должен зависеть от компонента A.
(Я должен отметить, что в отсутствие какого-либо фактического определения, которое я вижу, я думаю о «компоненте» как о эквивалентном Java-пакету, хотя я думаю, что могут быть применены те же мыслительные процессыесли «компонент» представляет собой отдельную услугу - оба должны предоставлять пользователям стабильные используемые интерфейсы, вызываемые локально или по сети)
В этом утверждении не представлено никаких доказательств, и оно не самоочевидно длямне.Рассмотрим случай класса ClassA
в компоненте (пакете) ComponentA
, который вызывает DoStuffReturn doStuff(DoStuffInput input, String someOtherArg)
в ClassB
в компоненте ComponentB
- и в котором вызов происходит либо через прямую зависимость, либо через зависимость от интерфейса в ComponentB
( не в ComponentA
, как советует Чистая архитектура)
- Если изменение в B является функциональным изменением, а не подписьюизменить (т. е. - для одного и того же входа
DoStuffInput
и String
, возвращается другое DoStuffReturn
), тогда не нужно никаких изменений в вызове A: ClassA
к ClassB.doStuff
остается действительным (те же аргументы и тип возвращаемого значения) ClassA
модульные тесты (которые должны использовать фиктивные ClassB
с) все равно должны проходить - Любые функциональные / интеграционные тесты, которые проверяют, какДля
ClassA
и ClassB
совместной работы потребуется обновить их ожидания, но это не изменится на ComponentA
(если только эти тесты не равны в ComponentA
- я, как правило, видел их вComponentAIntegrationTests
пакет, но я думаю, что они также могут быть совмещены. Это не кажетсябыть тем, о чем говорится в книге - похоже, речь идет об изменениях в коде, а не в тестах)
- Если изменение в B является изменением сигнатуры метода, отличного от
doStuff
, тогда A не потребует никаких изменений - Если изменение в B является изменением подписи на
doStuff
, то A потребует изменения - но это будет иметь местоесли интерфейс был и в A тоже.
(Обратите внимание, что в соответствии с настройкой Чистая архитектура выступает за, где интерфейс для предоставляющего класса находится в потребляющем Компоненте (A), только в первом случаебудет представлять собой изменение в B - так что это единственное, что нам действительно нужно беспокоиться)
Чего мне не хватает?Если ComponentA зависит от ComponentB, при каких обстоятельствах изменение класса в ComponentB потребует изменения в ComponentA?
(Обратите внимание, что я не выступаю против использования интерфейсов - онииметь множество других преимуществ, не в последнюю очередь позволяющих одновременную разработку "по обе стороны" контракта и позволяющих заменять различные реализации интерфейса)