views:

63

answers:

3

Hello,

I have the following design decision to make - running into it for the second time, so I hope I'm not the only one...

I have various Command classes that represent some actions. Each Command class (there are many, say SleepCommand, RunCommand, MeasureCommand) has different parameters with default values and so on, but in the end each has a generate() method that returns some packed representation.

Now, I also have a CommandSequence class that aggregates many commands in a list. It has its own generate() method that does some custom work, runs generate() for all the commands in its list, adds stuff of its own and so on.

I have a doubt of how to create these command objects. There are several alternatives:

(1) Let the user create instances of the various Command objects and just call an add_command() method on CommandSequence (2) Create a "factory method" in CommandSequence for each of the possible Commands. The factory methods accepts the Command's arguments and passes them to the Command constructor. (3) Since method (2) duplicates code (the constructor initialization lists of the objects), I can just make all the factory methods accept *args, **kwargs and pass them to the objects.

Now, approach (1) doesn't duplicate code, but exposes the plethora of Command classes to the user. As opposed to (2) the user code is more verbose:

# seq is a CommandSequence object
seq.add_command(SleepCommand(time='10 ms', wakeup_alarm=False))

as opposed to:

seq.sleep(time='10 ms', wakeup_alarm=False)

Since I'm building a kind of a DSL, user code shortness and clearness is very important to me. I can use method (3) to save on code duplication, but then where to place the documentation of the Command classes - in the factory methods or in the classes' constructors themseves.

Am I missing some obvious pattern here? How would you approach this?

+3  A: 

I'd stick with 1.

When you have a part of the code that is required to execute the SleepCommand, it makes sense to me to expose the construction interface of the SleepCommand to that part of the code. How else can they construct one?

You could use the factory, but I don't think it gains you much here (apart from code duplication as you point out). It also means that every time you need to add a new command, you need to go and change the CommandSequence class to expose an interface to execute the new command. This violates the open-closed principle (http://en.wikipedia.org/wiki/Open/closed%5Fprinciple).

Edit: Another important point is that number 1 is a simpler design. To add a new command, just create a new command class - this is good OO-design. Also, with the 2 lines of code you write in the question, I don't think the second is really much more complicated than the first.

Steg
+1  A: 

One more option is,

Step 1. Create a constructor of CommandSequence that takes in multiple command objects through variable argument lists

Step 2. Create static const (I am not a python prog, so please forgive if that is not possible) objects of type CommandSequence "inside" CommandSequence initialized with commands for the commonly used command sequences.

This will help clients in writing code like

CommandSequence.XYZCommandSeq.generate

Helps avoid duplication in many places.

Tanmay
A: 

I would avoid (3). You loose on documentation ... so you would have to duplicate something...

For (1), you mention the plethora of objects that the calling developper need to know. But these objects are exactly what the calling developper need to know, so it seem like a necessary complexity.

For (2), as you say, it is duplication.
Additionnaly, for each new command, you will have to maintain your factory methods...

KLE