liveBook · Manning

Separation of concerns

As explained in C++ Concurrency in Action, Second Edition

Separation of concerns is a software design principle that organizes a system so distinct concerns are handled independently.

Separation of concerns keeps related code together and unrelated code apart. When distinct responsibilities must run at the same time, separate threads can let each responsibility proceed without repeatedly handing control to the others. The threads still interact where their tasks require it. section 1.2.1

Playback and user input

A DVD player must decode and play media while responding to Pause or Quit. In one thread, playback code must regularly check for input. With separate threads, playback and the user interface can follow their own control flow; a Pause request becomes a direct interaction between them. The interface can respond promptly even if playback work is still underway. section 1.2.1

This is division by task type, not division of identical work across chunks of data. Each thread specializes in a responsibility, such as handling incoming events or doing ongoing work. It avoids a single loop that must run a little of every task and make each task save its state before yielding. Separation does not eliminate coordination: shared data can still complicate reasoning. section 8.1.3 section 4.4.2

An ATM split by responsibility

One thread can control the physical machinery, another the ATM logic, and a third bank communication. They can exchange messages instead of sharing application data. The logic then reacts to received events, updates its state, and sends requests to the appropriate thread. In C++, keeping data private this way requires discipline because threads share an address space. section 4.4.2

Listing 4.15. A simple implementation of an ATM logic class
struct card_inserted
{
    std::string account;
};
class atm
{
    messaging::receiver incoming;
    messaging::sender bank;
    messaging::sender interface_hardware;
    void (atm::*state)();
    std::string account;
    std::string pin;
    void waiting_for_card()                              #1
    {
        interface_hardware.send(display_enter_card());   #2
        incoming.wait()                                  #3
            .handle<card_inserted>(
                [&](card_inserted const& msg)            #4
                {
                    account=msg.account;
                    pin="";
                    interface_hardware.send(display_enter_pin());
                    state=&atm::getting_pin;
                }
                );
    }
    void getting_pin();
public:
    void run()                                           #5
    {
        state=&atm::waiting_for_card;                    #6
        try
        {
            for(;;)
            {
                (this->*state)();                        #7
            }
        }
        catch(messaging::close_queue const&)
        {
        }
    }
};


!@%STYLE%@!
{"css":"{\"css\": \"font-weight: bold;\"}","target":"[[{\"line\":12,\"ch\":57},{\"line\":12,\"ch\":59}],[{\"line\":14,\"ch\":57},{\"line\":14,\"ch\":59}],[{\"line\":15,\"ch\":57},{\"line\":15,\"ch\":59}],[{\"line\":17,\"ch\":57},{\"line\":17,\"ch\":59}],[{\"line\":28,\"ch\":57},{\"line\":28,\"ch\":59}],[{\"line\":30,\"ch\":57},{\"line\":30,\"ch\":59}],[{\"line\":35,\"ch\":57},{\"line\":35,\"ch\":59}]]"}
!@%STYLE%@!

The ATM logic class makes the boundary concrete: it waits for an incoming message, handles a card-inserted event, sends a display request, and changes its state. section 4.4.2

How C++ Concurrency in Action, Second Edition covers it

How deep it goes
Explains task-based thread division and illustrates message-driven state changes, without giving a general procedure for assigning responsibilities.
Examples
Illustrates the principle with DVD playback and user input, then an ATM divided among machinery, logic, and bank communication.
What you can do after
Practice/perform it by assigning distinct tasks to threads and identifying where they must exchange messages or events.
How it connects
Connects separation of concerns to the single responsibility principle, concurrency, message passing, and state machines.

Also explained in

Compare how 22 books explain separation of concerns →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of C++ Concurrency in Action, Second Edition. The section links point to the parts of the book it draws on.