Progress28.ru

IT Новости


09ae9cb0
3 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Java project structure

Создаём приложение с чистой архитектурой на Java 11

Всё больше внимания уделяется программам с чистой архитектурой. Рассказываем и показываем, как разрабатывать такие приложения на языке Java.

Архитектура программного обеспечения − важная тема программной инженерии последних лет. На практике реализация приложений с чистой архитектурой часто вызывает затруднения. Мы не затрагиваем паттерны или алгоритмы: под прицелом другие проблемы с точки зрения Java-разработчиков.

Перед погружением в код посмотрим на архитектуру:

  • Объекты: это самый стабильный код приложения, который не должен подвергаться внешним изменениям. Объектами могут быть методы и структуры данных.
  • Сценарии использования: здесь происходит инкапсуляция и внедряется бизнес-логика.
  • Адаптеры интерфейса: здесь располагаются сущности, происходит преобразование и представление данных в сценарии использования.
  • Фреймворки и драйверы: эта область содержит инструменты и фреймворки для запуска приложения.
  • Каждый уровень ссылается только на нижний и ничего не знает о существовании уровней выше
  • Сценарии использования и сущности − это сердце вашего приложения, у них минимальный набор зависимостей от внешних библиотек

Реализация

Мы будем использовать Gradle и модули Java Jigsaw для обеспечения зависимости между различными слоями.

Сделаем простое приложение, чтобы понять работу архитектуры. Вот некоторые из его функций:

  • создание пользователя;
  • поиск пользователя;
  • обработка списка всех пользователей;
  • авторизация пользователя с паролем.

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

Так выглядит проект:

Давайте погрузимся в разработку.

Внутренние уровни

Наши объекты и сценарии разделены на два проекта: «domain» и «usecase».

Эти пакеты − сердце нашего приложения.

Архитектура должна быть четкой. Из приведенного выше примера становятся понятными расположение и тип операций. При создании одиночного сервиса трудно определить его назначение и приходится погружаться в реализацию. В чистой архитектуре достаточно взглянуть на пакет usecase, чтобы увидеть список поддерживаемых операций.

Пакет entity содержит все сущности. В нашем случае будет только один пользователь:

Модуль usecase содержит бизнес-логику. Начнем с простого сценария FindUser:

У нас есть две операции, которые извлекают пользователей из хранилища, что стандартно для сервис-ориентированной архитектуры.

UserRepository − это интерфейс, который не реализован в текущем проекте. Это деталь нашей архитектуры, а детали реализовываются на внешних уровнях. Мы реализуем UserRepository при создании сценария использования (например, с помощью внедрения зависимости). Это даёт нам следующие преимущества:

  • Бизнес-логика не изменяется в зависимости от реализации.
  • Любое изменение в реализации не затрагивает бизнес-логику.
  • Очень легко вносить серьезные изменения в реализацию.

Сделаем первую итерацию сценария CreateUser:

Как и в сценарии FindUser, нам нужен репозиторий, способ генерации идентификатора и способ кодирования пароля. Всё это − детали, которые будут реализованы позже на внешних уровнях.

Нам нужно проверить данные пользователя и убедиться в том, что он не был создан ранее. Пишем последнюю итерацию сценария:

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

Последний сценарий LoginUser довольно прост и доступен на GitHub.

Для обеспечения границ оба пакета используют модули Jigsaw. Они позволяют не раскрывать детали реализации. Например, нет причин раскрывать класс UserValidator:

Ещё раз о роли внутренних уровней:

  • Внутренние уровни содержат объекты и бизнес-логику. Это самая стабильная часть приложения.
  • Любое взаимодействие с внешним миром (например, с базой данных или внешним сервисом) не реализовывается во внутренних уровнях.
  • Не используются фреймворки и минимальные зависимости.
  • Модули Jigsaw скрывают детали реализации.

Внешние уровни

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

Начнем с репозитория.

Репозиторий

Реализация UserRepository с простым HashMap:

Другие адаптеры

Адаптеры реализованы таким же способом, как интерфейс, объявленный в domain, и доступны на GitHub:

Собираем все вместе

Теперь нужно собрать детали реализации вместе. Для этого создаём папку с конфигурацией приложения и папку с кодом для запуска приложения.

Так выглядит конфигурационный файл:

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

Ниже представлен класс запуска приложения:

Веб-фреймворки

Если потребуется использовать Spring Boot или Vert.x, нужно:

  • Создать новую конфигурацию веб-приложения.
  • Создать новый ApplicationRunner.
  • Добавить контроллеры в папку адаптера. Контроллер отвечает за связь с внутренними уровнями.
Читать еще:  Как восстановить виндовс из папки windows old

Вот как выглядит контроллер Spring:

Полный пример этого приложения для Spring Boot и Vert.x доступен на GitHub.

Заключение

В этой статье мы продемонстрировали приложение с чистой архитектурой.

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

Плюсы

  • Мощность: ваша бизнес-логика защищена, и ничто извне не выведет её строя. Ваш код не зависит от внешнего фреймворка, контролируемого кем-то другим.
  • Гибкость: любой адаптер можно заменить в любое время любой другой реализацией на выбор. Переключение Spring boot/Vert.x происходит очень быстро.
  • Отложенные решения: можно построить бизнес-логику, не зная деталей о будущих БД и фреймворках.
  • Высокий уровень поддержки: легко определить компонент, вышедший из строя.
  • Быстрая реализация: архитектура позволяет сосредоточиться и уменьшить стоимость разработки.
  • Тесты: модульное тестирование проводится легче, так как зависимости четко определены.
  • Интеграционные тесты: можно реализовать любой внешний сервис для использования во время интеграционных тестов. Например, если не хочется платить за базы данных в облаке, можно использовать реализацию адаптера в памяти.

Минусы

  • Обучение: архитектура может стать непреодолимым препятствием для джуниоров.
  • Больше классов, больше пакетов, больше проектов, и с этим ничего не сделать. Изучайте другие языки, например, Kotlin. Он поможет уменьшить количество создаваемых файлов.
  • Высокая сложность проекта.
  • Не подходит маленьким проектам.

Понравилась статья о разработке приложений с чистой архитектурой? Другие статьи по теме:

Источник: Создание приложений с чистой архитектурой на языке программирования Java 11 на Medium

Основы программирования на Java

Структура программы

Основным строительным блоком программа на языке Java являются инструкции (statement). Каждая инструкция выполняет некоторое действие, например, вызовы методов, объявление переменных и присвоение им значений. После завершения инструкции в Java ставится точка с запятой (;). Данный знак указывает компилятору на конец инструкции. Например:

Данная строка представляет вызов метода System.out.println , который выводит на консоль строку «Hello Java!». В данном случае вызов метода является инструкцией и поэтому завершается точкой с запятой.

Кроме отдельных инструкций распространенной конструкцией является блок кода. Блок кода содержит набор инструкций, он заключается в фигурные скобки, а инструкции помещаются между открывающей и закрывающей фигурными скобками:

В этом блоке кода две инструкции, которые выводят на консоль определенную строку.

Выполнение программы. Метод main

Java является объектно-ориентированным языком, поэтому всю программу можно представить как набор взаимодествующих между собой классов и объектов. В первой главе при создании первого приложения программа была определена следующим образом:

То есть основу нашей программы составляет класс Program. При определении класса вначале идет модификатор доступа public , который указывает, что данный класс будет доступен всем, то есть мы сможем его запустить из командной строки. Далее идет ключевое слово class , а затем название класса. После названия класса идет блок кода, в котором расположено содержимое класса.

Входной точкой в программу на языке Java является функция main , которая определена в классе Program. Именно с нее начинается выполнение программы. Он обязательно должен присутствовать в программе. При этом его заголовок может быть только таким:

При запуске приложения виртуальная машина Java ищет в главном классе программы метод main с подобным заголовком, и после его обнаружения запускает его.

Вначале заголовка метода идет модификатор public , который указывает, что метод будет доступен извне. Слово static указывает, что метод main — статический, а слово void — что он не возвращает никакого значения. Далее в скобках у нас идут параметры метода — String args[] — это массив args, который хранит значения типа String, то есть строки. При запуске программы через этот массив мы можем передать в программу различные данные.

После заголовка метода идет его блок, который содержит набор выполняемых инструкций.

Комментарии

Код программы может содержать комментарии. Комментарии позволяют понять смыл программы, что делают те или иные ее части. При компиляции комментарии игнорируются и не оказывают никакого влияние на работу приложения и на его размер.

В Java есть два типа комментариев: однострочный и многострочный. Однострочный комментарий размещается на одной строке после двойного слеша //. А многострочный комментарий заключается между символами /* текст комментария */ . Он может размещаться на нескольких строках. Например:

BestProg

Пакеты. Использование пакетов в Java. Директивы import и package . Компилированные модули ( *.java ). Промежуточные .class файлы. Структура проекта. Использование стандартных библиотек Java

Содержание

1. Пакеты в Java

Каждый проект на языке Java в своей работе использует так называемые пакеты. Пакет – это отдельный модуль (пространство имен), которому соответствует одноименный каталог (папка). Пакет содержит библиотеки (группы) классов. Эти классы объединены между собой в одном пространстве имен или пакете. Количество реализованных классов в пакете неограничено. Классы, которые объявляются в пакете, реализуются в файлах с расширением *.java .

Читать еще:  Как восстановить удаленные сообщения вк через телефон

Пакеты могут иметь разнообразные уровни вложения (подкаталоги, подпапки). На разных уровнях вложения пакетов имена могут повторяться. Сохранение пакетов и файлов *.java в проекте осуществляется в виде древовидной структуры файловой системы.

2. Как подключить пакет к существующему проекту в Java Eclipse? Структура проекта, который содержит пакеты

В системе Java Eclipse подключение пакета есть стандартной командой. Предварительно может быть создан проект. В проекте может размещаться произвольное количество пакетов. В каждом пакете может быть произвольное количество классов.

Для создания пакета в некотором проекте используется команда

В результате откроется окно, изображенное на рисунке 1. В окне указывается имя проекта и пакета, который будет размещаться в этом проекте.

Рис. 1. Окно «New Java Package» . Создание пакета с именем MyPackage в проекте MyProject03

После выбора кнопки Finish происходит следующее. Проекту с именем MyProject03 соответствует специальная папка (каталог) с таким же именем. В этом каталоге создается несколько подкаталогов (подпапок). Один из них подкаталог с именем src. В этом подкаталоге создается каталог MyPackage , соответствующий создаваемому пакету.

3. Какая общая форма вызова класса из пакета? Пример

Чтобы обратиться к имени класса из пакета используется следующая общая форма:

  • PackageName – имя пакета, в котором реализован класс с именем ClassName ;
  • ClassName – имя класса.

Если пакет PackageName1 содержит подпапку (подпакет) с именем PackageName2 , то доступ к классу с именем ClassName имеет вид:

Подобным образом формируется доступ к классу в случае более более сложных уровней вложения.

Например. Пусть задан пакет с именем Figures . В пакете реализованы два класса с именами Circle и Rectangle . Тогда, фрагмент кода, который создает объекты этих классов будет следующий:

4. Пример, который демонстрирует использование одного имени класса в разных пакетах и в разных каталогах (папках)

На рисунке 2 изображена иерархия, которую могут образовывать пакеты.

Рис. 2. В проекте MyProject03 три пакета с именами MyPackage01 , MyPackage02 , MyPackage02.MyFolder02

Как видно из рисунка, в проекте MyProject03 созданы 3 пакета с именами MyPackage01 , MyPackage02 , MyPackage03 . Каждый из пакетов имеет класс с одинаковым именем MyClass01 . Таким образом происходит разделение одинаковых имен.

Ниже приведен демонстрационный код, создающий объекты классов MyClass01 , которые размещены в разных пакетах (папках), согласно схеме из рисунка 2.

Как видно из программного кода, доступ к классу из пакета осуществляется с помощью разделителя ‘ . ‘ (точка). С целью сокращения весьма больших строк доступа к имени класса, используется директива import .

5. Какое назначение директивы import ? Общая форма. Пример

Директива import позволяет сократить весьма длинные строки обращений к классам (интерфейсам, перечислениям), которые реализованы в пакетах.

В простейшем случае общая форма директивы import следующая:

  • PackageName – имя пакета, который подключается;
  • ClassFileName – имя файла с расширением *.java , в котором реализован класс или группа классов, к которому нужно обращаться по сокращенному имени.

Например. Пусть в некотором проекте задан пакет Figures. В пакете Figures реализован один класс с именем Circle . Класс размещен в файле «Circle.java» . Исходный код файла Circle.java , который реализует класс Circle, имеет вид:

Чтобы из любого другого класса, который размещается в другом пакете данного проекта, иметь доступ к методам файла Circle.java из пакета Figures нужно в начале указать:

Ниже приводится пример подключения и использования класса Circle из другого пакета и другого класса.

В вышеприведенном коде создается объект класса Circle с именем c . Однако, благодаря директиве import , название класса указывается в удобном (сокращенном) виде:

Если в пакете OtherPackage убрать строку

то объект класса Circle нужно было бы создавать следующим образом

6. Как в директиве import указать, что могут быть использованы все классы из данного пакета?

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

  • Package – имя пакета с библиотекой подключаемых классов.
Читать еще:  Как восстановить сообщение в вк если удалил
7. Что собой представляет ключевое слово package ?

Ключевое слово package задает имя библиотеки, в которую могут входить разное количество компилированных модулей. Компилированные модули – это файлы с расширением .java .

Если в модуле (файле с расширением .java ) указывается строка наподобие

то это означает, что данный модуль есть частью библиотеки с именем PackageName .

8. Что такое компилированный модуль в Java?

Каждый компилированный модуль – это файл, который содержит исходный код на языке Java. Этот файл имеет расширение «*.java» . Имя файла совпадает с именем открытого ( public ) класса, который реализован в этом модуле. Это требование есть обязательным.

Каждый компилированный модуль может содержать не большее одного открытого класса. Компилированный модуль входит в состав пакета. В пакете может быть любое количество компилированных модулей.

9. Какие требования к реализации компилированного модуля ( .java )?

К реализации компилированного модуля предъявляются следующие требования:

  • имя компилированного модуля должно иметь расширение .java ;
  • имя компилированного модуля без учета расширения .java должно совпадать с именем открытого ( public ) класса, который реализован в этом модуле;
  • имя компилированного модуля и имя реализованного в нем открытого класса должны начинаться из большой буквы;
  • в компилированном модуле может быть не более одного открытого ( public ) класса. Если в компилированном модуле есть еще другие (вспомогательные) классы, то они должны быть скрытыми (без модификатора public ).
10. Сколько классов могут быть реализованы в компилированном модуле?

Компилированный модуль может иметь любое количество классов. Однако, открытым ( public ) должен быть только один класс. Имя этого класса должно совпадать с именем компилированного модуля.

11. Какое назначение файлов с расширением .class ?

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

Например, в языке программирования Delphi временные скомпилированные файлы имели расширение *.dcu . В языке программирования C++ временные скомпилированные файлы имели расширение .obj . После компоновки из этих файлов получался *.exe файл (исполняемый модуль).

После этого, файлы с расширением .class объединяются в пакет и сжимаются утилитой JAR . Это все осуществляет интерпретатор Java.

12. Какие преимущества дает использование ключевых слов package и import ?

Использование ключевых слов package и import позволяет удобно поделить пространство имен таким образом, чтобы предотвратить возникновению конфликтов между разными разработчиками классов на Java.

13. Пример, который демонстрирует структуру проекта и размещение компилированных модулей

Пусть в Java Eclipse создан проект с именем Project03 . Проект размещается в папке по умолчанию, которая используется системой Java Eclipse. В проекте созданы 2 пакета с именами Figures , OtherPackage . В пакетах реализованы по одному компилированному модулю соответственно с именами Circle.java и MyClass.java . Рисунок 3 отображает структуру проекта Project03 системы Java Eclipse.

Рис. 3. Отображение структуры проекта Project03 в окне Package Explorer

В файле (модуле) Circle.java разработан класс Circle . В файле MyClass.java разработан класс MyClass . В классе MyClass из пакета OtherPackage (см. рис. 3) подключается модуль Circle.java с помощью директивы

Если компилятор встречает директиву import , он формирует полное имя с учетом пути к каталогу, который записан по умолчанию в системе.

таким образом, получается полное имя модуля Circle.java :

По желанию путь к каталогу (папке) по умолчанию можно изменить.

14. Пример подключения класса из стандартной библиотеки Java

Чтобы обратиться к имени класса из стандартной библиотеки Java используется один из двух способов:

  1. Использование полной формы или полного имени класса в библиотеке.
  2. Использование сокращенной формы имени класса. В этом случае, полное имя пакета с нужным классом, может быть задано явным образом с помощью директивы import .

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

  • PackageName – имя пакета или стандартной библиотеки Java (например, util, math, applet и прочее). Пакеты или библиотеки могут иметь уровни вложения. Если библиотеки содержат вложения, то они разделяются символом ‘ . ‘ ;
  • ClassName – имя класса (или интерфейса), который реализован в библиотеке PackageName (например ArrayList , BigDecimal и т.п.).

Например. Фрагмент кода, который использует класс Vector из стандартной библиотеки Java. В данном случае стандартная библиотека Java носит имя java.util .

В вышеприведенном коде происходит обращение к классу Vector по полному имени:

Ссылка на основную публикацию