1

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?

1
  • 1
    Since you are talking about a method on a CommandSequence object, I would have thought add is just as clear as add_command, which makes your first option a bit less verbose. Personally, if it makes sense, I would make CommandSequence a subclass of list, or expose the same interface as a list, for maximum usefulness and minimum learning. Commented Oct 6, 2009 at 9:55

3 Answers 3

3

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_principle).

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

0

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...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.