Skip to content

Latest commit

 

History

History
127 lines (91 loc) · 2.69 KB

README.md

File metadata and controls

127 lines (91 loc) · 2.69 KB

bowen

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.

Most Recent Release

With Leiningen:

[savagematt/bowen "2.0"]

Usage

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

Limitations

  • Only works on protocols, not interfaces
  • Will produce completely inscrutable error messages if you get the syntax wrong

License

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.