From: Massimiliano Mirra Date: 2002-05-25T22:14:00+09:00 Subject: Design question - Internals of a Command class I am (re)writing a base class that defines a command as issued by a user on the command line. I'm pretty sold on an external interface which is simply: Command#exec(argstr) It is used like this: class Join < Command ... end join = Join.new join.exec("source1 source2 destination") I'm not as sure about the internals. Right now it goes like this: def exec(argstr) args = parse(argstr) results = run(*args) out(*results) end protected def parse(argstr) return argstr.split end def run(*args) results = args.collect {|arg| "Processed #{arg}"} return results end def out(*results) puts results end Each step (parse, run) returns its results instead of setting instance variables (think of it like an internal contract between methods), so, when subclassing, one only needs to override the method where the command specifics happen. class Join < Command protected def parse(argstr) sources = argstr.split dest = sources.pop [sources, dest] end def run(sources, dest) File.open(dest, 'w') do |d| sources.each do |source| d.write File.open(source) {|s| s.read} end end ["Done."] end end Simple, uh? Yet, I've lost count of how many headaches this ``data passing'' between those three methods has caused me. Often I would return an array that still needs to be seen as an array from the next step, and would forget to enclose it in [brackets], then the next step *explodes it and goes SNAFU. Other times I would pass a single result and the next step would still try to explode it. Bugs are also incredibly difficult to track down. I tried to put a patch on it with the following: def exec(argstr) args = parse(argstr).to_a results = run(*args).to_a out(*results) end Now returning e.g. a string from the run method would not break anything because it would still arrive like ["string"] in the out method. Yet, of course, this leads to more head banging when returning arrays, not to mention returning hashes (whose to_a has a completely different meaning) and other classes which may well have a to_a of their own. Now, what I'd like to ask to the three or four patient souls who have managed to read so far, is: given the design requirement that in subclasses one should override as little as possible (no matter if this is achieved by an ``internal contract'' as I did or by other means), do you have any idea of how it can be accomplished in a fool-proof way that avoids the above head bangings? (The fool being I, if there was any doubt left.) By the way, there is a lot more to this than I simplified here. For example, it all lives in a context of a command line input/output loop, and ability to quickly tie such an interface and set of commands to a model. Kind of an interface toolkit, so to speak, only the tools are commands instead of widgets. In case I sparkled the interest of anyone, I will be glad to torture him off list with messy code -- er -- share opinions and ideas. Massimiliano