Создание расширяемой архитектуры

TL;DR

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

В этой статье мы реализуем программу, основанную на наборе простых требований. Сначала мы реализуем это с помощью полиморфизма подтипов, а затем с помощью сопоставления с образцом. Учитывая эти реализации, мы добавляем еще два требования и сравниваем необходимые изменения для реализации новых требований.

Отказ от ответственности

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

Начальные требования

Давайте рассмотрим пример двухмерных фигур. Мы хотим разрешить два разных типа: прямоугольники и круги. У всех фигур есть периметр и площадь. Прямоугольник определяется двумя сторонами с индивидуальной длиной. Его периметр (x + y) * 2, а площадь x * y. Круг определяется радиусом. Его периметр 2 * PI * r, а площадь PI * r * r.

Использование полиморфизма подтипа

Давайте смоделируем этот пример с помощью полиморфизма подтипов в Java.

Мы моделируем форму как интерфейс. У него есть метод для получения периметра и метод для получения области.

Прямоугольник - это реализация интерфейса Shape.

Круг - это вторая реализация интерфейса Shape.

Эта иерархия классов успешно реализует требования. Теперь наши заинтересованные стороны могут вычислить периметр и площадь прямоугольников и кругов. Большой.

Дополнительные требования

Теперь наши заинтересованные стороны выдвигают два дополнительных требования.

  1. Они хотят вычислить эти свойства и для параллелограммов.
  2. Они хотят вычислить диаметр фигуры. Это самое большое расстояние внутри фигуры.

Добавление нового класса в архитектуру подтипа

С нашей архитектурой данного подтипа удовлетворить первое требование легко.

Мы начинаем с создания класса Parallelogram и добавляем предложение implements в сигнатуру класса.

На этом этапе компилятор Java уже уведомляет нас о том, что в классе отсутствуют реализации методов perimeter и area. IDE дает нам возможность создавать заглушки методов.

Теперь мы можем добавить атрибуты к классу и реализовать оба метода.

Полиморфизм подтипа позволил нам выполнить это требование с помощью одного нового файла. Нам не нужно было трогать существующую кодовую базу.

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

Добавление нового метода в архитектуру подтипа

Давайте посмотрим на второе требование.

  • Заинтересованные стороны хотят вычислить диаметр формы. Это самое большое расстояние внутри фигуры.

Добавляем новый метод в интерфейс Shape.

На этом этапе компилятор Java уведомляет нас в классах Rectangle, Shape и Parallelogram. В каждом классе отсутствует реализация нового метода diameter.

Мы добавляем реализацию метода diameter в класс Rectangle.

И в класс Circle.

Ааа и в класс Parallelogram.

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

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

Использование сопоставления с образцом

Давайте посмотрим на другой подход: сопоставление с образцом. Сопоставление с образцом в настоящее время недостаточно поддерживается в Java. (Вероятно, это изменится в следующих версиях: JEP 305, JEP 354.) В настоящее время мы будем использовать Kotlin для подхода к сопоставлению с образцом. Kotlin обеспечивает достаточную поддержку сопоставления с образцом, чтобы продемонстрировать этот пример.

Фигура моделируется как класс sealed без каких-либо свойств. Ключевое слово sealed предписывает, чтобы все подклассы Shape были объявлены в одном файле. Мы определяемRectangleиCircle как единственные подклассы класса Shape.

Новая иерархия похожа на иерархию с использованием полиморфизма подтипов. Ключевое отличие состоит в том, что мы не указываем методы в иерархии классов. Вместо этого мы определяем методы как функции вне иерархии.

Давайте реализуем функцию area.

Поскольку функция определена вне иерархии, мы не знаем, является ли переданная форма прямоугольником или кругом. Мы используем сопоставление с образцом, чтобы различать два случая. Если фигура является подтипом Rectangle, компилятор преобразует аргумент shape в прямоугольник и оценивает shape.x * shape.y. Если фигура является подтипом Circle, компилятор преобразует аргумент shape в круг и вычисляет PI * shape.radius * shape.radius.

Обратите внимание, что нет необходимости определять предложение по умолчанию, поскольку класс Shape - это sealed. Если один класс не будет охвачен, компилятор уведомит нас.

Аналогичным образом мы можем реализовать функцию perimeter.

Мы добавляем два предложения для двух реализаций: прямоугольники и круги.

Добавление нового метода в архитектуру сопоставления с образцом

Мы успешно выполнили первоначальные требования. Теперь рассмотрим одно из дополнительных требований.

  • Заинтересованные стороны хотят вычислить диаметр формы. Это самое большое расстояние внутри фигуры.

В новой архитектуре выполнить это требование так же просто, как добавить новую функцию.

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

Сопоставление с образцом упрощает добавление новых функций.

Добавление нового класса в архитектуру сопоставления с образцом

Рассмотрим другое требование.

  • Заинтересованные стороны хотят вычислить эти свойства и для параллелограммов.

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

Сначала мы добавляем новый подкласс Shape.

На этом этапе компилятор сообщает нам, что в выражениях when в функциях perimeter, area и diameter отсутствует предложение для Parallelogram.

Добавляем недостающий регистр в функцию perimeter.

И к функции area.

Ааа и к функции diameter.

Чтобы добавить новый класс Parallelogram, нам нужно было коснуться каждой существующей функции. Изменения распределяются по всему приложению.

Сопоставление с образцом усложняет добавление новых типов сущностей.

Резюме

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

Что вы думаете?

Ресурсы