Interface design where functions need to be called in a specific sequence

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?

3

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.

5

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.

3

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.

6

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.

1

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

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

1

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.

1

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.

3

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật