Design question - Internals of a Command class 
Author Message
 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



Wed, 10 Nov 2004 21:19:17 GMT  
 
 [ 1 post ] 

 Relevant Pages 

1. CPython internal design question

2. Class hierarchy design question

3. Design Question: Module -vs- Class Methods

4. dbi and class design question

5. Question about class design

6. Design question.... parent/child class

7. Design question: HotDraw classes Figure & Drawing

8. Design question: C command calling back into Tcl

9. Internal Commands

10. How can I used a internal DOS command like COPY

11. internal timing commands for f2c (benchmark tests)

12. Accessing the output of an internal command

 

 
Powered by phpBB® Forum Software