(newbie-ish) Modelling question - multi methods?

89 views
Skip to first unread message

Colin Yates

unread,
Aug 12, 2013, 9:39:02 AM8/12/13
to clo...@googlegroups.com
Hi all,

I have a strategy that defines handling a woosey.  Most woosey handlers are going to need some help, maybe a wibbly and a woobly.  In OO land I would have a WooseyHandler { void handle(Woosey woosey); }.  The implementations would then receive Wibblies and Wooblies via dependency injection.

So far I have gotten away with passing in collaborators as parameters to the functions, or if there are a lot of collaborators for ThingX I will have a factory for ThingX which returns a map of collaborators that ThingX needs and then pass that map as the first argument to ThingX's functions.

My question is now I have a genuine strategy - which seems to point to multi-methods.  Let's say that ThingX can now handle wooseys.  Each instance of ThingX is really just a map of its collaborators so that map can't take part in the multimethod implementations.  I could make ThingX a record and then extend it but I am trying to avoid types as much as possible...

So, how can I handle this situation?  A strategy where the implementation of that strategy requires more than the subject of that strategy?

Being well entrenched in Java I am consciously avoiding the 'kingdom of nouns' and wrapping everything in a custom type, and so far it is working wonderfully.  Am I now too allergic to custom types?

Thanks!

Col

Russell Mull

unread,
Aug 12, 2013, 11:50:00 AM8/12/13
to clo...@googlegroups.com
Generally, I'd go for a simple strategy (ahem) like this:

(defn make-handler [wibblie wooblie]
  (fn [woosy]
    <eloquent code here>))

But perhaps there's something about your case that I don't understand; I'm not entirely sure where multimethods need to come into it, unless you need to change which handler you're using based on the thing it's handling. 

Russell

Colin Yates

unread,
Aug 12, 2013, 12:05:01 PM8/12/13
to clo...@googlegroups.com
Hi Russell,

Maybe the concrete case will help.  I have a single entry point into which Commands can be posted, let's call it a CommandGateway.  This gateway will do many things, the least of which will be to delegate the handling of the command to the command handler registered with the gateway.  The strategy is 'how to handle a command'.

Now, the handler for :command-a needs a database for example.  The handler for :command-b needs another collaborator.

CommandGateway has no idea about this and defines a defmulti called handle-command which dispatches on the :type of the command (which is really a trivial map).

The command handler for :command-a registers a defmethod for :command-a but how does the body of that defmethod access the database connection for example?

I can see a few options:
 - give up on avoiding global state and have a bound 'system' register (i.e. a global/static service locator) - please no.
 - don't use multi-methods for this and have CommandGateway have a map of predicate:handler.  The handler for command-a would then register itself and use lexical scoping to access the required collaborators
 - use multi-methods, but have a CommandAHandler which is a stateful record and retains its collaborators (i.e. the database) and extends itself to implement command-handler)

My question is really 'when does using types stop being idiomatic'.  So far I have gotten surprisingly far using maps/sequences and passing collaborators around.  Now it seems to use multi methods to dispatch the command I need the target of the dispatch to be stateful to remember its collaborators.  

Might all be a storm in a teacup :), hence the clarity request.



--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/hRKZTclcllQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Jonah Benton

unread,
Aug 13, 2013, 12:55:25 AM8/13/13
to clo...@googlegroups.com
It sounds like it depends on whether the collaborators are properly known to the command, e.g. at command generation/creation time, or to the handler, e.g. at handler creation/registration time.

If the former, then it would make sense for each command to be a record that was created with all required collaborators and participated in a Handle protocol, called by the gateway.

if the latter, then handlers are simple functions that take collaborators as initial parameters, followed by a command parameter. Then the bindings for collaborators are wrapped in a partial prior to registration, so the gateway calls the registered function with the command map as the single parameter.

A factor by which to decide- do the collaborators remain the same per command for the lifetime of the app? If the same, then assigning them every time a command is created is wasteful, but if different, then assigning per command is required.

That said, an issue I've run into when using types like this is that the type distinction required for dispatch is complected with field membership of the records. When the same fields need to be handled in potentially different ways, then different command records need to have the same fields; or when different fields need to be handled the same way, either different command records dispatch to the same handler, or a single record contains the union of fields needed for the handler. All of those are unpleasant, so unless the handler functions neatly map to distinct collections of fields, just using maps is more appealing.



You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages