Let's start from the very beginning.
I believe that the three following points represent the most important aspects to consider when choosing appropriate implementation of Command Pattern in Scala.
- what the signature of "Command" used as a parameter looks like
- how do you construct a "Command" instance
- how do you assign a "Command" instance to a value
// Example 1
def command(i : Int) = println(i);
// Usage:
// 1
def invoker(cmd : Int => Unit) = cmd(1)
// 2
invoker(command _)
// or
invoker(command)
// 3
val savedCommand = command _
However concise and elegant it has important shortcomings. Firstly you get very wide and crude interface - (Int) => (Unit) means nothing after all. You won't communicate a lot of information with that and a user can pass any command to an "invoker" method (including ShutDownReactor procedure ;]). The second thing is a nasty wildcard you have to use when assigning an instance of a command to a variable. (which you may optionally use in 2. - and if you don't compiler will do it for you).
You can have a bit cleaner solution with following piece of code.
// Example 1
def command = (i : Int) => println(i);
// Usage:
// 1
def invoker(cmd : Int => Unit) = cmd(1)
// 2 a) prepared command
invoker(command)
// 2 b) ad hoc command
invoker((i : Int) => println(i))
// 3
val savedCommand = command
The method presented above is actually the partially applied function of a function from the previous example. You gain the advantage of not writing the '_' (placeholder) when assigning an instance to a variable - what IMHO both simplifies and makes it a little less magical :). And if you wonder what would happen if you've added it anyway, the answer is simple - you'd get a partially applied partially applied function ;P with following signature:
() => (Int) => Unit
To solve the issue of wide interface you might use the more classical example of a Command pattern with a bit of functional language coolness on the top ;)
// Example 3
trait Command { def apply(i : Int) }
object DefaultCommand extends Command { def apply(i : Int) = println(i) }
// Usage:
// 1
def invoker(cmd : Command) = cmd(1)
// 2
invoker(DefaultCommand)
// 2 b) ad hoc command construction
invoker(new Command { def apply(i : Int) = println(i) })
// 3
val savedCommand : Command = DefaultCommand
Pretty nice - it gives you very clean nice interface with possibility of extending it with whatever responsibility you like (what was obviously missing in first two examples). Nothing comes for free though - the ad hoc construction of a command class is more verbose than a simple command function.
If you're cra^M^M^Mbrave enough you might even try something like this:
class CrazyCommand extends (Int => Unit) { def apply(i : Int) = println(i) }
And now the nice thing is that you may both use it as in 3. example and also pass it to a function defined in previous code snippets. Though I have no idea why anyone would like to do that ;)
While I'm at it I'd remind you about a cool Scala feature that let you extend class behaviour at instantiation and thus you can do the same with:
new Command with (Int => Unit) { def apply(i: Int) = println(i) }
Fun Exercise! ;)
type CoolCommand = (Int => Unit) with (String => Unit)
def coolFunction(cmd : CoolCommand) { cmd(1); cmd("one") }
The implementation of a class that may be passed as a parameter to a 'coolFunction' is left to a reader and may be impossible :P
No comments:
Post a Comment