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







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

Теперь вы можете задаться вопросом, зачем мне создавать свою собственную версию? Что ж, причина проста. У каждого свой уникальный опыт и подходы к решению проблем. Делясь своей версией, я не обесцениваю идеи других. Вместо этого я предлагаю свою точку зрения на то, как я вижу проблему и решение, которое лучше всего работает для меня. Вы можете найти в ней что-то родственное или извлечь из нее новые идеи, и в этом и заключается цель обмена — внести свой вклад в коллективные знания и поощрить различные точки зрения в сообществе разработчиков программного обеспечения.

Эта статья соответствует принципам серии Fluent and Fun Clean Architecture Series, которую я буду отстаивать. Если у вас еще не было возможности прочитать ее, не беспокойтесь — это не обязательное условие для понимания этой статьи. высокоуровневое объяснение того, почему я воссоздаю свой вариант использования.

Во-первых, что такое вариант использования?

В разработке программного обеспечения в целом вариант использования — это описание конкретного действия или взаимодействия, которое система или приложение выполняет для достижения определенной цели для субъекта (пользователя или другой системы). В нем описываются «шаги» и взаимодействия между различными компонентами, чтобы продемонстрировать, как конкретная функциональность используется в системе. Варианты использования часто используются для сбора «функциональных требований» и определения поведения системы с точки зрения ее пользователей.

В контексте чистой архитектуры термин «вариант использования» относится к конкретному архитектурному компоненту, представляющему бизнес-операцию или задачу, которую выполняет приложение. необходимо выполнить. Он является частью уровня предметной области и действует как посредник между уровнем представления (UI) и уровнем предметной области. Вариант использования отвечает за организацию потока данных и бизнес-логики для достижения желаемого результата.

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

Итак, что такое UseCase в «Свободной и увлекательной чистой архитектуре»?

В архитектуре Fluent and Fun Clean вариант использования служит более комплексной цели по сравнению с традиционными вариантами использования. Вместо того чтобы просто описывать функциональность с точки зрения пользователя, мы подходим к ней с точки зрения приложения при выполнении события или действие.

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

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

Как создать вариант использования в беглой и увлекательной чистой архитектуре?

Прежде чем мы углубимся в вариант использования Fluent and Fun Clean Architecture, давайте сначала разберемся, как реализовать его в Clean Architecture.

Для лучшего понимания рассмотрим простой пример варианта использования в приложении электронной коммерции:

Случай использования: Разместить заказ

Действие: Клиент

Описание. Этот вариант использования представляет собой процесс размещения клиентом заказа на продукт в приложении электронной коммерции.

Требования:

  1. Клиент должен авторизоваться.
  2. Корзина не пуста, чтобы можно было оформить заказ.
  3. Сумма денег в кошельке покупателя не должна быть меньше суммы заказанного товара.
  4. Если все три соблюдены, обновите товарный запас.
  5. Очистите корзину в приложении после завершения оформления заказа.

Чтобы реализовать это в чистой архитектуре, вы обычно следуете структуре кода, подобной той, что показана ниже.

class PlaceOrderUseCase(
    private val userRepository: UserRepository,
    private val productRepository: ProductRepository
) {
    operator fun invoke(order: Order) {
        if (userRepository.isLoggedIn()) {
            val cart = userRepository.getCart()
            if (cart.isNotEmpty()) {
                if (userRepository.hasEnoughFunds(order.getTotalPrice())) {
                    productRepository.updateProductStock(order)
                    userRepository.clearCart()
                } else {
                    throw InsufficientFundsException(
                        "Not enough funds in the wallet."
                    )
                }
            } else {
                throw EmptyCartException("The cart is empty.")
            }
        } else {
            throw NotLoggedInException("User is not logged in.")
        }
    }
}

Теперь давайте разберем его шаг за шагом, чтобы создать вариант использования:

  1. Создайте класс с именем, состоящим из глагола в настоящем времени + существительного/что (необязательно) + UseCase.
  2. Определите необходимые параметры, которые могут быть репозиториями или другими вариантами использования, в зависимости от требований вашего приложения.
  3. В Kotlin вы можете сделать экземпляры класса Use Case вызываемыми как функции, определив функцию invoke() с модификатором operator.

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

В контексте Fluent and Fun Clean Architecture цель состоит в том, чтобы добиться аналогичной реализации, как описано ниже.

OrderUseCase.kt

fun OrderRepository.placeOrder(order: Order) {
    whenUserLoggedIn {
        whenCartNotEmpty {
            withSufficientFunds {
                updateProductStock(order)
                clearCart()
            }
        }
    }
}

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

Вот разбивка реализации:

  1. Вместо использования класса мы создали файл с именем «OrderUseCase».
  2. Имя файла следует соглашению существительное/что (необязательно) + UseCase.
  3. Класс не задействован; вместо этого мы использовали функцию расширения верхнего уровня или обычную функцию верхнего уровня.
  4. Сама функция представляет вариант использования.
  5. Имя функции служит основным вариантом использования для вызова из уровня представления.

Почему я предпочитаю такой вариант использования?

  1. Читаемость и выразительность. Благодаря использованию цепочки вызовов функций с осмысленными именами (whenUserLoggedIn, whenCartNotEmpty, withSufficientFunds) код становится более читабельным и выразительным. Каждый шаг процесса четко очерчен, что облегчает понимание потока варианта использования.
  2. Упрощенный поток управления. Цепочка вызовов функций соответствует естественному потоку условий, которые необходимо выполнить для размещения заказа. Это упрощает поток управления и делает логику более простой.
  3. Модульность: код разделяет задачи на разные функции (whenUserLoggedIn, whenCartNotEmpty, withSufficientFunds), заставляя каждую функцию обрабатывать определенную проверку или условие. Это способствует модульности и упрощает изменение или добавление новых проверок, не влияя на весь вариант использования.
  4. Краткость: в коде нет необходимости в отдельном классе прецедентов с шаблонным кодом. Он сжимает логику в краткую и сфокусированную функцию расширения.
  5. Свободный интерфейс. Использование ключевого слова when в именах функций (whenUserLoggedIn, whenCartNotEmpty) придает коду плавный стиль интерфейса, повышая удобочитаемость и делая его более похожим на естественный язык.
  6. Ясность намерений. Разбивка логики вариантов использования на отдельные функции позволяет разработчику четко выражать свои намерения на каждом этапе процесса. Становится очевидным, что представляет собой каждое условие в контексте размещения заказа.

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

Почему вам не нужен класс для реализации варианта использования?

Это правильный вопрос. Прежде чем дать ответ, давайте начнем с определения того, что такое класс в программировании.

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

Что такое состояние и поведение в классе?

  • Состояние представляет текущие данные или свойства каждого объекта. Он представлен переменными экземпляра (полями) внутри класса, и каждый объект, созданный из класса, имеет свой собственный набор этих переменных. Например, в классе Car атрибуты состояния могут включать цвет, марку, модель, год выпуска и уровень топлива, причем каждый экземпляр имеет свои определенные значения.
class Car(private val color: String, 
          private val make: String, 
          private val model: String, 
          private val year: Int) {
    private var fuelLevel: Double = 0.0
}
  • Поведение в классе определяет действия или операции, которые могут выполнять объекты. Он представлен методами внутри класса. Эти методы определяют, как объекты взаимодействуют с другими и внешним миром. Например, в классе Car у нас могут быть такие методы, как start(), Accel(), Brake(),иrefuel() для представления различных вариантов поведения объекта автомобиля в зависимости от его состояния.
class Car {
    private var fuelLevel: Double = 0.0

    // Behavior (methods)
    fun start() {
        println("Car has started.")
    }

    fun accelerate() {
        println("Car is accelerating.")
    }

    fun brake() {
        println("Car is braking.")
    }

    fun refuel(amount: Double) {
        fuelLevel += amount
        println("Car has been refueled with $amount gallons.")
    }
}

Отвечая на вопрос:

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

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

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

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

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

Заключение

В заключение, в контексте Fluent and Fun Clean Architecture мы изучили другой подход к реализации вариантов использования, который предлагает преимущества с точки зрения удобочитаемости, выразительности и модульности. Используя функции расширения верхнего уровня или обычные функции верхнего уровня, мы можем создавать варианты использования, которые читаются как естественный язык, делая логику мгновенно понятной и легкой для понимания.

Отдавая предпочтение функциям, а не классам для вариантов использования, мы упрощаем поток управления и избегаем ненужного стандартного кода, делая кодовую базу более лаконичной и целенаправленной. Этот функциональный подход согласуется с принципами Fluent and Fun Clean Architecture, продвигая более выразительную и адаптируемую кодовую базу.

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

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

Следите за новостями и примерами того, как Fluent and Fun Clean Architecture может изменить наш подход к сценариям использования и разработке программного обеспечения в целом.