Предположим, что я определяю функцию Хаскеля f (либо чистое, либо действие), а где-то внутри f я вызываю функцию g. Например:
f = ...
g someParms
...
Как заменить функцию g макетной версией для модульного тестирования?
Если бы я работал в Java, g был бы методом класса SomeServiceImpl
, реализующим интерфейс SomeService
. Затем я использую инъекцию зависимостей, чтобы сказать f, чтобы использовать SomeServiceImpl
или MockSomeServiceImpl
. Я не уверен, как это сделать в Haskell.
Это лучший способ сделать это, чтобы ввести класс типа SomeService:
class SomeService a where
g :: a -> typeOfSomeParms -> gReturnType
data SomeServiceImpl = SomeServiceImpl
data MockSomeServiceImpl = MockSomeServiceImpl
instance SomeService SomeServiceImpl where
g _ someParms = ... -- real implementation of g
instance SomeService MockSomeServiceImpl where
g _ someParms = ... -- mock implementation of g
Затем переопределите f следующим образом:
f someService ... = ...
g someService someParms
...
Кажется, что это сработает, но я просто изучаю Haskell и думаю, что это лучший способ сделать это? В общем, мне нравится идея инъекции зависимостей не только для насмешек, но и для того, чтобы сделать код более настраиваемым и многоразовым. Как правило, мне нравится идея не быть заблокирована в одну реализацию для любой из услуг, которые использует часть кода. Было бы неплохо использовать вышеупомянутый трюк в коде, чтобы получить преимущества инъекции зависимостей?
EDIT:
Давайте сделаем еще один шаг. Предположим, что в модуле есть ряд функций a, b, c, d, e и f, которые должны быть способны ссылаться на функции g, h, я и j из другого модуля. И предположим, что я хочу быть в состоянии издеваться над функциями g, h, я и j. Я мог бы четко передать 4 функции в качестве параметров для a-f, но это немного боль, чтобы добавить 4 параметра ко всем функциям. Кроме того, если мне когда-либо понадобилось изменить реализацию любого из a-f для вызова еще одного метода, мне нужно будет изменить его подпись, что может создать неприятное упражнение по рефакторингу.
Любые трюки, чтобы сделать этот тип ситуации легко работать? Например, в Java я мог бы построить объект со всеми его внешними службами. Конструктор будет хранить службы в переменных-членах. Затем любой из этих методов мог получить доступ к этим службам через переменные-члены. Таким образом, поскольку методы добавляются к службам, ни одна из сигнатур метода не изменяется. И если нужны новые службы, изменяется только подпись метода конструктора.