Ответ 1
В "общих терминах" вы бы назвали это if/else на стероидах и в стиле "произвести впечатление", который вы назвали бы им с "динамической отправкой", "полином времени выполнения" и т.д.
Предположим, вы хотите определить функцию "Добавить", которая должна работать для разных типов данных, например, в случае Int, она должна добавить числа, в случае строк она должна конкатцировать строки. Теперь его очень просто реализовать, используя if else, в основном вы проверяете тип аргументов и, если они являются целыми числами, затем добавляйте их, если они являются строками, тогда concat их еще генерирует исключение. Но проблема заключается в том, что если вы хотите добавить поддержку нового типа данных в свою функцию добавления, вам нужно будет изменить функцию добавления, что может быть невозможно в тех случаях, когда вы не выполняете контролировать источник добавления, например, в случае, если он определен в некоторой библиотеке и т.д.
defmulti
и defmethod
позволяет решить эту проблему, добавив новый случай к существующей функции без изменения ее кода.
(defmulti add (fn [a b] [(type a) (type b)]))
add - это имя функции, анонимная функция - это ваш if/else, в основном это будет вызываться в ваших аргументах добавления, и возвращаемое значение этой функции будет проверяться, если есть какая-либо реализация. Теперь давайте реализуем его для Integer.
(defmethod add [Integer Integer] ([a b] (+ a b)))
[Integer Integer]
- это регистр сортировки по возвращаемому значению анонимной функции, определенной в defmulti
, а затем реализации.
Аналогично мы можем сделать для строк
(defmethod add [String String] ([a b] (str a b)))
Вызов его целыми числами
(add (int 1) (int 2))
Вызов со строками
(add "hello" "world")
Вызов его с чем-то, что не соответствует нашему if/else. i.e еще не реализация для случая приведет к исключению