Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

The task is to configure a piece of hardware within the device, according to some input specification. This should be achieved as follows:

1) Collect the configuration information. This can happen at different times and places. For example, module A and module B can both request (at different times) some resources from my module. Those 'resources' are actually what the configuration is.

2) After it is clear that no more requests are going to be realized, a startup command, giving a summary of the requested resources, needs to be sent to the hardware.

3) Only after that, can (and must) detailed configuration of said resources be done.

4) Also, only after 2), can (and must) routing of selected resources to the declared callers be done.


A common cause for bugs, even for me, who wrote the thing, is mistaking this order. What naming conventions, designs or mechanisms can I employ to make the interface usable by someone who sees the code for the first time?

share|improve this question
    
Stage 1 is better called discovery or handshake ? –  rwong 13 hours ago
    
Temporal coupling is an anti-pattern and should be avoided. –  Snowman 9 hours ago

9 Answers 9

It's a redesign but you can prevent misuse of many APIs but not having available any method that shouldn't be called.

For example, instead of first you init, then you start, then you stop

Your constructor inits an object that can be started and start creates a session that can be stopped.

Of course if you have a restriction to one session at a time you need to handle the case where someone tries to create one with one already active.

Now apply that technique to your own case.

share|improve this answer
    
zlib and jpeglib are two examples that follow this pattern for initialization. Still, plenty of documentations are necessary to teach the concept to developers. –  rwong 18 hours ago
2  
This is exactly the right answer: if order matters, each function returns a result that can then be called on to perform the next step. The compiler itself is able to enforce the design constraints. –  Snowman 9 hours ago

You can have the startup method return an object that is a required parameter to the configuration:

Resource *MyModule::GetResource();
MySession *MyModule::Startup();
void Resource::Configure(MySession *session);

Even if your MySession is just an empty struct, this will enforce through type safety that no Configure() method can be called before the startup.

share|improve this answer
    
+1 this is the most flexible way to do it. –  Mehrdad 9 hours ago

Building on the Answer of Cashcow - why do you have to present a new Object to the caller, when you can just present a new Interface ? Rebrand-Pattern:

class IStartable     { public: virtual IRunnable      start()     = 0; };
class IRunnable      { public: virtual ITerminateable run()       = 0; };
class ITerminateable { public: virtual void           terminate() = 0; };

You can also let ITerminateable implement IRunnable, if a session can be run multiple times.

Your object:

class Service : IStartable, IRunnable, ITerminateable
{
  public:
    IRunnable      start()     { ...; return this; }
    ITerminateable run()       { ...; return this; }
    void           terminate() { ...; }
}

// And use it like this:
IStartable myService = Service();

// Now you can only call start() via the interface
IRunnable configuredService = myService.start();

// Now you can also call run(), because it is wrapped in the new interface...

In this way you can only call the right methods, since you have only the IStartable-Interface in the beginning and will get the run() Method only accessible when you have called start(); From the outside it looks like a pattern with multiple classes and Objects, but the underlying class stays one class, which is always referenced.

share|improve this answer
1  
What is the advantage of having just one underlying class instead of several? As this is the only difference with the solution I proposed, I would be interested in this particular point. –  Michael Grünewald 16 hours ago

Use a builder-pattern.

Have an object which has methods for all the operations you mentioned above. However, it doesn't perform these operations right away. It just remembers each operation for later. Because the operations aren't executed right away, the order in which you pass them to the builder doesn't matter.

After you defined all the operations on the builder, you call an execute-method. When this method is called, it performs all the steps you listed above in the correct order with the operations you stored above. This method is also a good place to perform some operation-spanning sanity-checks (like trying to configure a resource which wasn't set up yet) before writing them to the hardware. This might save you from damaging the hardware with a nonsensical configuration (in case your hardware is susceptible to this).

share|improve this answer

You just need to document correctly how the interface is used, and give a tutorial example.

You may also have a debugging library variant which does some runtime checks.

Perhaps defining and documenting correctly some naming conventions (e.g. preconfigure*, startup*, postconfigure*, run*....)

BTW, a lot of existing interfaces follow a similar pattern (e.g. X11 toolkits).

share|improve this answer
    
A state transition diagram, similar to the Android application activity lifecycle, may be necessary to convey the information. –  rwong 13 hours ago

This is indeed a common and insidious kind of error, because compilers can only enforce syntax conditions, while you need your client programs to be "grammatically" correct.

Unfortunately, naming conventions are almost entirely ineffective against this kind of error. If you really want to encourage people not to do ungrammatical things, you should pass out a command object of some kind that must be initialized with values for the preconditions, so that they can't perform the steps out of order.

share|improve this answer
    
Do you mean something like this? –  Vorac 18 hours ago

There is a lot of valid approaches to solve your problem. Basile Starynkevitch proposed a “zero-bureaucracy” approach which leaves you with a simple interface and relies on the programmer using appropriately the interface. While I like this approach, I will present another one which has more eingineering but allows the compiler to catch some errors.

  1. Identify the various states your device can be in, as Uninitialised, Started, Configured and so on. The list has to be finite.¹

  2. For each state, define a struct holding the necessary additional information relevant to that state, e.g. DeviceUninitialised, DeviceStarted and so on.

  3. Pack all treatments in one object DeviceStrategy where methods use structures defined in 2. as inputs and outputs. Thus, you may have a DeviceStarted DeviceStrategy::start (DeviceUninitalised dev) method (or whatever the equivalent might be according to your project conventions).

With this approach, a valid program must call some methods in the sequence enforced by the method prototypes.

The various states are unrelated objects, this is because of the substitution principle. If it is useful to you to have these structures share a common ancestor, recall that the visitor pattern can be used to recover the concrete type of the instance of an abstract class.

While I described in 3. a unique DeviceStrategy class, there is situations where you may want to split the functionality it provides across several classes.

To summarise them, the key points of the design I described are:

  1. Because of the substitution principle, objects representing device states should be distinct and not have special inheritance relations.

  2. Pack device treatments in startegy objects rather than in the objects representing devices themselves, so that each device or device state sees only itself, and the strategy sees all of them and express possible transitions between them.

I would swear I saw once a description of a telnet client implementation following these lines, but I was not able to find it again. It would have been a very useful reference!

¹: For this, either follow your intuition or find the equivalence classes of methods in your actual implementation for the relation “method₁ ~ method₂ iff. it is valid to use them on the same object” — assuming you have a big object encapsulating all the treatments on your device. Both methods of listing states give fantastic results.

share|improve this answer
public class Executor {

private Executor() {} // helper class

  public void execute(MyStepsRunnable r) {
    r.step1();
    r.step2();
    r.step3();
  }
}

interface MyStepsRunnable {

  void step1();
  void step2();
  void step3();
}

Using this pattern you are sure that any implementor will execute in this exact order. You can go one step further and make an ExecutorFactory which will build Executors with custom execution paths.

share|improve this answer

Use a state machine to track what can and should be done, and when.

share|improve this answer
    
this reads more like a comment than an answer. See How to Answer –  gnat 8 hours ago
    
Hi @gnat. I just read your link and unfortunately it gives no further guidance an what separates an answer from a comment. maybe you could point out something specific on that page that would lead to the above being better as a comment? My answer includes two links to the definition of a state machine and a DrDobbs article on state machines in C++ the authors language of choice. Succinct doesn't necessarily equate to comment. –  Paddy3118 5 hours ago
    

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.