Легко реализовать шаблон, который может принести большую пользу вашей кодовой базе.
В этой статье, после базового определения, я покажу, как писать различные типы VO, а затем попытаюсь проиллюстрировать некоторые быстрые победы, полученные с помощью этого шаблона, на основе ситуаций, с которыми я часто сталкиваюсь.
Объект значения
Я впервые узнал о шаблоне Value Object (VO) в книге Domain Driven Design среди других строительных блоков для уровня предметной области.
Я часто описываю их как обертку примитивных типов, позволяющую указать имя и поведение.
Краткое определение
Это объект, равенство которого основано на всех свойствах.
Это означает, что он имеет только одно состояние и не может быть изменен. Следовательно, он неизменен.
Выполнение
На большинстве языков это легко реализовать.
Класс
Простой пример: VO, представляющий положительное число с классом данных.
У класса должны быть равные / хэш-методы, геттеры (но не сеттеры)
Проверка (не null, формат и т. Д.) Параметров выполняется в конструкторе, и если это не так, он завершается ошибкой, вызывая исключение.
Enum
Другой пример: использование VO для представления валют с помощью перечисления.
Поведение
К ВО можно добавить дополнительную логику. Например, amount
может иметь операции.
Вы можете заметить, что создается новый экземпляр с новым значением.
Сочинение
Наконец, мы можем скомпоновать их в другой ВО.
Вы можете заметить, что методы содержат проверку параметров, как в конструкторе. Затем он делегирует операции Amount
.
Я добавляю несколько фабричных методов для лаконичного создания экземпляра. Может пригодиться в тестовом коде.
Преимущества
1. Сильный набор текста
Это не то же самое, что иметь
rateCalculation(age: int)
чем иметь
rateCalculation(age: Age)
В первом случае метод должен будет проверить, действителен ли параметр (положительный)
Во втором случае эта логика содержится в ВО `Age`, и метод rateCalculation может сосредоточиться на его ответственности.
Более того, факт централизации ограничений на значения позволяет избежать где-то возможных ошибок по невнимательности.
2. Явные концепции
Это не то же самое, что иметь
class Product { // … double price String currency }
чем иметь
class Product { // … Price price } class Price { double amount Currency currency } enum Currency { USD, EUR, CHF, … }
В первом случае ничто не указывает на то, что два свойства среди других сильно связаны.
Во втором случае VO Price
явно указывает, что он состоит из суммы и валюты.
3. Больше никаких классов Utils;)
Это не то же самое, что иметь
isChild(age:int)
чем иметь
age.isChild()
В первом случае, поскольку мы используем примитивный тип, мы часто будем использовать класс Utils со статическим методом. Не очень ООП
Во втором случае у нас есть метод непосредственно на подержанном автомобиле, который проще в использовании.
4. Больше никаких грязных тестов.
Это не то же самое, что иметь
class PersonTest{ //… large setup //… lots of tests @Test fun ageFromBirthDate(){ val person = Person(‘’, ’’, Date(20,APRIL, 1991), …) person.age shouldBe 30 } @Test fun ageFromBirthDate_currentYear(){ val person = Person(‘’, ’’, Date(20,APRIL, 2021), …) person.age shouldBe 0 } @Test fun ageFromBirthDate_dependingOnDay(){ val person = Person(‘’, ’’, Date(20,DECEMBER, 1991), …) person.age shouldBe 29 } //… other tests }
чем иметь
class AgeTest{ @Test fun fromDate(){ val age = Age.from(Date(20,APRIL, 1991)) age shouldBe Age(30) } @Test fun ageFromBirthDate_currentYear(){ val age = Age.from(Date(20,APRIL, 2021)) age shouldBe Age(0) } @Test fun ageFromBirthDate_dependingOnDay(){ val age = Age.from(Date(20,DECEMBER, 1991)) age shouldBe Age(29) } }
В первом случае есть много отвлекающих факторов, которые могут затруднить внесение изменений:
- тесты на возраст входят в число других несвязанных тестов в файле из нескольких сотен строк
- Инициализация человека частичная, потому что требуется только часть свойств
Во втором случае отдельный текстовый класс содержит конкретные варианты использования с. Тесты краткие, содержат только необходимую информацию. А на самом деле тестов в PersonTest меньше.
Эти 3 небольших примера делают ваш код и тесты более читаемыми и удобными. Повторяя эти рефакторинги везде, где вы можете, ваш код станет легче писать, и в нем будет меньше ошибок, несмотря на наличие большего количества классов.
Когда начать?
Другие довольно распространенные варианты использования, помимо денег:
- Номер телефона
- Возраст
- Период
- Процент
- Цена
- Геолокация
- везде у вас есть ограничения по стоимости
В большинстве случаев я нахожу практические примеры в кодовой базе, глядя на:
- Классы Utils или Helper
- Крупные компании с более чем 10 объектами недвижимости