Implements the decorator pattern for clojure protocols.
You can think of it as middleware for protocols.
When I have a protocol with multiple methods, often I want to wrap an existing implementation, overiding some but not all methods, like this:
(defprotocol Sheep
(baa [this])
(eat [this])
(walk-around [this]))
(deftype LoudSheep [decorated-sheep]
Sheep
(baa [this]
(clojure.string/upper-case (baa decorated-sheep))
; Just delegate to the decorated instance
(eat [this]
(eat decorated-sheep))
(walk-around [this]
(walk-around decorated-sheep)))
Providing explicit implementations of eat
and walk-around
is tedious. I want to be able to do this instead:
(decorate
(deftype LoudSheep [decorated-sheep]
Sheep
(baa [this]
(clojure.string/upper-case (baa decorated-sheep))))
Bowen allow you to do this for defrecord
, deftype
and reify
.
With Leiningen:
[savagematt/bowen "2.0"]
Given these protocols:
(defprotocol Talky
(sayhello [this])
(echo [this s]))
(defprotocol Goodbye
(goodbye [this]))
And this type that we want to decorate:
(deftype Talker []
Talky
(sayhello [this] "hello")
(echo [this s] s)
Goodbye
(goodbye [this] "goodbye from the decorated talker"))
We can do this:
(decorate
(deftype PoliteTalker [decorated-talker]
Talky
; Delegate to the decorated instance and add our own behaviour
(sayhello [this] (str "*ahem* " (sayhello decorated-talker)))
; Don't call the decorated instance at all
(echo [this s]
(str "excuse me, did you say '" s "'?"))
Goodbye
; Just delegates to decorated-talker
))
(let [talker (PoliteTalker. (Talker.)]
(sayhello talker)
;=> "*ahem* hello"
(echo talker "cheese")
;=> "excuse me, did you say 'cheese'?"
(goodbye talker)
;=> "goodbye from the decorated talker"
)
Bowen works the same way for defrecord
.
To use decorate
, around a reify
form, provide the instance to decorate as the first parameter:
(let [talker (Talker.)]
(decorate talker
(reify
Talky
(sayhello [this] (str "*ahem* " (talker)))
Goodbye))
See core_test.clj for detailed usage examples
- Only works on protocols, not interfaces
- Will produce completely inscrutable error messages if you get the syntax wrong
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.