[Java] Улучшение модификаторов видимости Java с помощью ArchUnit (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
06-Апр-2021 14:32

Инкапсуляция и отделение внутренних компонентов от public, вероятно, является одним из наиболее недооцененных методов программирования, когда речь идет о обеспечении длительной поддержки приложения.К счастью, в Java есть нечасто используемый модификатор видимости package-private, который очень помогает скрыть нежелательные детали реализации. К сожалению, если количество внутренних классов велико, оно плохо масштабируется, но, к счастью, нам может помочь ArchUnit.Public vs PrivateРазделение private и public позволяет уменьшить связность и получить свободу изменения деталей реализации, не беспокоясь о внесении нежелательных критических изменений.
Примером из реальной жизни может служить одна из библиотек автора - параллельные сборщики. Благодаря минимизации размера общедоступного API у меня была возможность несколько раз реорганизовать внутреннюю архитектуру, не рискуя внести какие-либо критические изменения на уровне API.Если бы я этого не сделал, мои руки были бы связаны, так как кто-то мог бы использовать некоторые из моих внутренних классов напрямую.То же самое относится к любому другому модулю или классу, который должен взаимодействовать с чем-то еще.Возможности package-privateДля скрытия внутренней структуры пакета можно использовать модификатор package-private и ограничить видимость классов, к которым не следует иметь доступ извне пакета.Давайте посмотрим на типичную структуру пакета:
В этом варианте, к сожалению, нам нужно оставить все внутренние классы public, потому что видимость package-private работает в пределах одного пакета, а не всей иерархии.Учитывая это, если мы хотим использовать package-private, нам нужно будет разместить их все в одном пакете:
К сожалению, такой подход плохо масштабируется с увеличением количества классов.Представляем ArchUnitК счастью, у нас может иметься как внутренняя структура пакета, так и ограничения видимости благодаря библиотеке ArchUnit, которую можно использовать для обеспечения соблюдения архитектурных соглашений.Например, мы можем воссоздать функциональность иерархического модификатора package-private, ограничив доступ к классам в подпакетах com.pivovarit.movies классами, находящимися во всей иерархии пакетов:
public class ArchitectureTest {
    private static final JavaClasses classes = new ClassFileImporter()
      // ...
      .importPackages("com.pivovarit");
    @Test
    void com_pivovarit_movies_shouldNotExposeInternalClasses() {
        classes().that().resideInAPackage("com.pivovarit.movies.*")
          .should()
          .onlyBeAccessed().byClassesThat()
          .resideInAPackage("com.pivovarit.movies..")
          .check(classes);
    }
}
А теперь, если мы создадим класс вне пакета и будем использовать public API (Rentals), тесты будут зелеными:
package com.pivovarit;
import com.pivovarit.movies.Rentals;
public class Starter {
    public static void main(String[] args) {
        Rentals instance = Rentals.instance();
        boolean rent = instance.rent(42);
    }
}
Но если мы попытаемся получить доступ к MovieDetailsRepository напрямую, мы получим нарушение:
package com.pivovarit;
import com.pivovarit.movies.repository.MovieDetailsRepository;
public class Starter {
    public static void main(String[] args) {
        MovieDetailsRepository movieDetailsRepository
          = new MovieDetailsRepository();
    // java.lang.AssertionError: Architecture Violation
    }
}
Расширяя идеюЕстественно, ArchUnit можно использовать для обеспечения соблюдения множества соглашений, например, в проекте, упомянутом ранее, он используется для обеспечения соблюдения политики нулевых зависимостей:
@Test
void shouldHaveZeroDependencies() {
    classes().that().resideInAPackage("com.pivovarit.collectors")
      .should()
      .onlyDependOnClassesThat()
      .resideInAnyPackage("com.pivovarit.collectors", "java..")
      // ...
      .check(classes);
}
… Или обеспечить существование единственного public класса:
@Test
void shouldHaveSingleFacade() {
    classes().that().arePublic()
      .should().haveSimpleName("ParallelCollectors")
      .andShould().haveOnlyPrivateConstructors()
      .andShould().haveModifier(FINAL)
      // ...
      .check(classes);
}
Больше примеров использования ArchUnit можно найти на официальной странице.ЗаключениеОтделение внутренних компонентов от public - один из лучших способов повысить удобство сопровождения вашего программного обеспечения. К сожалению, собственные инструменты Java ограничены, но ArchUnit может охватить те случаи, когда Java не может.Конечно, приведенные выше примеры - это просто основа, на которой вы можете опираться. Реальная жизнь полна более сложных случаев.Тем не менее, ArchUnit может помочь вам улучшить ваш public API. Но ArchUnit не поможет, если вы продолжите добавлять все больше и больше исключений в свои правила.Примеры из этой статьи можно найти на GitHub.
===========
Источник:
habr.com
===========

===========
Автор оригинала: Grzegorz Piwowarek
===========
Похожие новости: Теги для поиска: #_java, #_api, #_arhitektura (архитектура), #_java
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 19:42
Часовой пояс: UTC + 5