Когда его спрашивают об Injection Dependency in Scala, довольно много ответов указывают на использование Reader Monad, либо из Scalaz, либо просто для вашего собственного. Существует ряд очень четких статей, описывающих основы подхода (например, Обсуждение рунаров, Jason blog), но мне не удалось найти более полный пример, и я не вижу преимуществ такого подхода, например, более традиционный "ручной" DI (см. руководство, которое я написал). Скорее всего, я пропустил какой-то важный момент, поэтому вопрос.
Как пример, предположим, что у нас есть эти классы:
trait Datastore { def runQuery(query: String): List[String] }
trait EmailServer { def sendEmail(to: String, content: String): Unit }
class FindUsers(datastore: Datastore) {
def inactive(): Unit = ()
}
class UserReminder(findUser: FindUsers, emailServer: EmailServer) {
def emailInactive(): Unit = ()
}
class CustomerRelations(userReminder: UserReminder) {
def retainUsers(): Unit = {}
}
Здесь я моделирую вещи, используя классы и параметры конструктора, которые очень хорошо сочетаются с "традиционными" подходами DI, однако этот дизайн имеет пару хороших сторон:
- каждая функциональность имеет четко перечисленные зависимости. Мы предполагаем, что зависимости действительно необходимы для правильной работы функциональности.
- зависимости скрыты по функциональным возможностям, например.
UserReminder
не знает, чтоFindUsers
нужен хранилище данных. Функциональность может быть даже в отдельных единицах компиляции. - мы используем только чистый Scala; реализации могут использовать неизменные классы, функции более высокого порядка, методы "бизнес-логики" могут возвращать значения, заключенные в монаду
IO
, если мы хотим захватить эффекты и т.д.
Как это можно было бы смоделировать с помощью монады Reader? Было бы неплохо сохранить вышеприведенные характеристики, чтобы было ясно, какие зависимости нужны каждой функциональности, и скрыть зависимости одной функциональности от другой. Обратите внимание, что использование class
es больше детализирует реализацию; возможно, "правильное" решение с использованием монады Reader будет использовать что-то еще.
Я нашел несколько смежный вопрос, который предлагает либо:
- с использованием одного объекта среды со всеми зависимостями
- с использованием локальных сред
- "parfait" pattern
- индексированные по типу карты
Однако, кроме того, что (но этот субъективный) слишком сложный, как для такой простой вещи, во всех этих решениях, например, метод retainUsers
(который вызывает emailInactive
, который вызывает inactive
для поиска неактивных пользователей), должен знать о зависимости Datastore
, чтобы иметь возможность правильно вызывать вложенные функции - или я ошибаюсь?
В каких аспектах использование Reader Monad для такого "бизнес-приложения" будет лучше, чем просто использование параметров конструктора?