Привет всем Android-разработчикам! Давайте разберем принципы SOLID, вернее, один из них.
Недавно я углубился в SOLID и нашел один скрытый потенциал. Теперь хочу рассказать про него вам, не углубляясь в теорию.
Вернемся в недалекое прошлое и с нуля создадим проект с помощью Gradle на языке Java без дополнительной конфигурации сборки. Структура сборки приведена ниже.
На момент написания статьи версия Gradle v6.8.3. С плагином Java функция инкрементной компиляции включена по умолчанию.
Теперь представим, что Client1
использует только метод m1
, Client2
— только метод m2
, а Client3
метод m3
класса Service
. Как вы думаете, что произойдет, если мы изменим тело m3
и еще раз скомпилируем проект?
Можно предположить, что если мы внесли бинарное совместимое изменение в тело метода, то скомпилироваться должен только Service
. Но в действительности компилируется весь проект.
Чтобы вывести результат сразу в Gradle, воспользуемся опцией listFiles
для Java компиляции. Она записывает все компилируемые файлы.
tasks.withType(JavaCompile) {
options.listFiles = true
}
Скомпилируем исходные Java-файлы с помощью JDK компилятора, вызвав задачу compileJava
. Как правило, ее запускают с задачей build
или assemble
.
./gradlew compileJava
Наконец, можем увидеть все скомпилированные файлы:
> Task :compileJava
Source files to be compiled:
/ISP_java/src/main/java/com/jurcik/ivet/service/Service.java
/ISP_java/src/main/java/com/jurcik/ivet/client/Client1.java
/ISP_java/src/main/java/com/jurcik/ivet/client/Client2.java
/ISP_java/src/main/java/com/jurcik/ivet/client/Client3.java
Почему так происходит?
Дело не в Gradle и его работе. Полученный результат — ожидаемое поведение Java. Ведь двоичная совместимость не влияет на список компилируемых исходных файлов.
Все объявления, которые должны быть импортированы в исходный код, создают зависимость кода. Таким образом, исходный код Client1
зависит от метода m2
и m3
, хоть он и не вызывает их. Поэтому, изменяя код m2
или m3
в классе Service
, Client1
будет еще раз скомпилирован, несмотря на то, что он не менялся.
Принцип разделения интерфейса спешит на помощь
Принцип разделения интерфейса следующий: ни один клиент не должен вынужденно зависеть от методов, которые он не использует.
Применим его и создадим для каждого метода отдельный интерфейс, который предоставит определенному клиенту только то, что ему нужно. Ниже проиллюстрирована структура:
Теперь еще раз внесем бинарное совместимое изменение в тело m3
класса Service
и скомпилируем проект.
./gradlew compileJava
Вот оно!
> Task :compileJavaSource files to be compiled:/ISP_java/src/main/java/com/jurcik/ivet/service/Service.java
Скомпилировался только Service
.
Что насчет Kotlin?
Компилятор Kotlin достаточно умный. Отслеживая изменения с помощью двоичного интерфейса приложений, он перекомпилирует только измененные файлы. Однако он не отменяет принцип SOLID, а лишь упрощает его выполнение.
Заключение
Каждый разработчик, знакомый с ООП наверняка слышал о принципах SOLID. Мы регулярно применяем их по несколько раз, не замечая этого. Несмотря на то, что они глубоко укоренились у нас в голове, им стоит уделять особое внимание. Ведь они не ограничены лишь архитектурой системы.
Читайте также:
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Iveta Jurčíková: Compile less with SOLID