Во многих случаях у меня есть набор функций, которые я хотел бы реализовать по-разному. Самый очевидный пример этого состоял бы в том, чтобы абстрагировать от определенных баз данных. На объектно-ориентированном языке Вы использовали бы интерфейс для этого:
interface DB {
ResultSet query(String query);
void persist(Object o);
...
}
В коде speudo я хотел бы сделать что-то вроде этого:
(ns dbbackend)
(abstractfn query [q])
(abstractfn persist! [o])
И затем реализации для каждой базы данных:
(ns dbbackend.mysql :implements dbbackend)
(defn query [q] ...)
(defn persist! [o] ...)
Мне не совсем ясно, что лучшая практика должна сделать что-то подобное на функциональном языке, конкретно Clojure. Я должен использовать мультиметоды для этого?
Теперь, когда была выпущена версия 1.1 Clojure , возможно, пора заглянуть в будущее.
Типы данных и протоколы , которые в настоящее время доступны только в новой главной ветке на github , могут быть именно тем, что вы ищете.
(defprotocol DB
(query [backend query])
(persist [backend object]))
(deftype MySQLBackend []
DB
(query [query] ...)
(persist [object] ...))
]Для препротокольной версии Clojure:[
] []Интерфейс:[
] [(ns dbbackend)
(defmulti query
{:arglists '([c q])}
suitable-dispatch-fn)
(defmulti persist!
{:arglists '([c o])}
suitable-dispatch-fn)
]
[]Реализация:[
] [(ns dbbackend.mysql
(:requires dbbackend))
(defmethod query com.mysql.jdbc.Connection
[c q]
...)
(defmethod persist! com.mysql.jdbc.Connection
[c o]
...)
]
[]Использование:[
] [(ns user
(:require dbbackend dbbackend.mysql))
(def mysql-connection (connect-to-mysql))
(query mysql-connection some-query)
]
[]Реальный пример такого подхода можно найти под капотом ClojureQL.[
].