Skip to content

deckchair-technicians/bowen

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

About

Implements the decorator pattern for clojure protocols

Resources

License

Stars

Watchers

Forks

Packages

No packages published