tags:

views:

226

answers:

3

I want to provide multiple implementations of a message reader/writer. What is the best approach?

Here is some pseudo-code of what I'm currently thinking:

  • just have a set of functions that all implementations must provide and leave it up to the caller to hold onto the right streams

    (ns x-format)
    (read-message [stream] ...)
    (write-message [stream message] ...)
    
  • return a map with two closed functions holding onto the stream

    (ns x-format)
    (defn make-formatter [socket]
      {:read (fn [] (.read (.getInputStream socket))))
       :write (fn [message] (.write (.getOutputStream socket) message)))})
    
  • something else?

+3  A: 

I think that read-message and write-message are utility functions. What you need to do is encapsulate your functions in a with- macro(s). See 'with-output-to-string' in common lisp to see what I mean.

Edit: When you use a with- macro you can have error handling and resource allocation in the macro expansion.

Gutzofter
+4  A: 

I think the first option is better. It's more extensible, depending how these objects are going to be used. It's easier to add or change a new function that works on an existing object if the functions and objects are separate. In Clojure there usually isn't much reason to bundle functions along with the objects they work on, unless you really want to hide implementation details from users of your code.

If you're writing an interface for which you expect many implementations, consider using multimethods also. You can have the default throw a "not implemented" exception, to force implementors to implement your interface.

As Gutzofter said, if the only reason you're considering the second option is to allow people not to have to type a parameter on every function call, you could consider having all of your functions use some var as the default socket object and writing a with-socket macro which uses binding to set that var's value. See the builtin printing methods which default to using the value of *out* as the output stream, and with-out-str which binds *out* to a string writer, as a Clojure example.

This article may interest you; it compares and contrasts some OOP idioms with Clojure equivalents.

Brian Carper
+1  A: 

I'd go with the first option and make all those functions multimethods.

Matthias Benkard