Бизнес-правила под соусом карри
Все эти кулинарные разговоры о кухне замыканий – это хорошо, но более бизнес-ориентированным читателям понравится следующий пример. Рассмотрим задачу расчета "чистой" цены определенной позиции Book
, учитывая скидку магазина и различные государственные налоги, например, налог на добавленную стоимость. Если вы включите эту логику в состав класса Book
, получившееся решение, вероятно, будет жестким. Если, например, магазин может изменить значение скидки, или захотеть применять ее только к некоторым позициям, то такое решение будет недостаточно гибким.
И как же быть? С помощью замыканий и карринга можно без труда приспособиться к изменению бизнес-правил. Вы можете использовать набор простых замыканий, представляющих отдельные бизнес-правила, и комбинировать их различными способами с помощью композиций. И, наконец, вы можете сопоставлять их с коллекциями с помощью шаблонов вычислений.
В листинге 8 показан пример книжного магазина. Замыкание rMultiply
представляет собой частичное приложение, которое преобразует бинарное умножение в унарное замыкание, используя постоянный второй операнд. Два замыкания calcDiscountedPrice
и calcTax
являются экземплярами замыкания rMultiply
с фиксированными значениями множителя. Замыкание calcNetPrice
представляет собой алгоритм расчета "чистой" цены, сначала умножающий цену на скидку, а затем - на налог на с продаж. И, наконец, к цене книги применяется calcNetPrice
.
Листинг 8. Бизнес-объект книги
import fp.*
class Book {
@Property name
@Property author
@Property price
@Property category
}
def bk = new Book(name : 'Groovy', author :
'KenB', price : 25, category : 'CompSci')
// постоянные
def discountRate = 0.1
def taxRate = 0.17
// замыкание книги
def calcDiscountedPrice = Functor.rMultiply.curry(1 - discountRate)
def calcTax = Functor.rMultiply.curry(1 + taxRate)
def calcNetPrice =
Functor.composition.curry(calcTax, calcDiscountedPrice)
// расчет чистой цены
def netPrice = calcNetPrice(bk.price)
println "netPrice: ${netPrice}" // netPrice: 26.325