头图
Command mode: introduce related concepts. Implementation reference last time Memento

Command Pattern

About this series of articles

In this talk about the XX mode series, I will not introduce all the 23 modes of GoF one by one, nor are they limited to GoF. Some patterns may not be necessary for template reuse, and some patterns are not included in GoF, so sometimes there will be supplementary versions of the text, like the last time talks about Observer in C++17 Mode-4-Signal slot mode is like this.

Because the focus of this series is on template implementation, with engineering practice as the goal, we will not introduce motivations, scenarios or the like (sometimes not necessarily) like general design pattern articles, but will Based on our experience and understanding of the model, to explain it in our own words, I think it might be useful, of course, it is stupid to do so in the fast-moving world.

For us and for individuals, this is also a process of review and rethinking. For you, it might actually be useful to look at other people's understanding from another angle.

About command mode

In the on Memento mode in C++17 , I mentioned that the memorandum mode and the command mode are often linked and coordinated, and are implemented in the traditional implementation and Undo Manager implementation (class library undo-cxx ) The center contains the command mode part.

So this article is meant to make up the numbers.

motivation

Command mode is a behavior mode. This design pattern abstracts a variety of actions into commands, and the Client executes these commands through the executor Caller/Invoker/Executor without worrying about the details of the call. A concrete command object ConcreteCommand is responsible for explaining all the details of the execution of the command, including the recipient of the command. Receiver is the recipient of the command execution. For example, in a word processor, the recipient is the currently selected text of the current editor, and the font style command will make style settings for the recipient.

The UML diagram described in this paragraph looks like this:

img

FROM: here: svg file

The other picture is very beautiful, take it here for comparison:

img

FROM: The Command Pattern - fjp.github.io

Scenes

In a restaurant, after a customer orders a meal, the ordering action can be regarded as the Client notifying the Executor that it is time to execute the order. The context in which the command is executed contains the menu (Receipt) of the customer's order. The menu is sent to the back kitchen and assigned to the appropriate chef to cook, which is equivalent to the specific execution of the order.

In a music player, Play, Pause, Stop, Forward, and Rewind are the corresponding commands. When the Invoker executes the command, the receiver, the recorder, is controlled.

In a word processor, the currently selected text of the current editor is usually regarded as the recipient, and user UI operations such as Bold, Italic, etc. will trigger the execution of the corresponding command.

The scene of vector mapping is similar to a word processor.

The menu and shortcut key system in the desktop window application is also a typical embodiment of the command mode.

Command mode is also very commonly used in game development and is basically a necessity.

The communication protocol parser is another command mode application scenario that you may not have thought about. The communication protocol usually includes a series of token recognition, a series of instruction instructions, and a series of data report information. These contents can be abstracted and organized into a command mode for specific processing.

Code

Regarding the design idea of the command mode, the key points are nothing more than the following points:

  1. How should the cmd_t class system be constructed, the management and destruction of object instances?
  2. Command grouping problem, that is, how to design composite_cmd_t
  3. Context problem: pre-curing a receiver is not feasible for a class library, so the receiver and even the sender can be placed in a so-called context container and passed during the execution of the command. The user of the class library can have the ability to extend this context container to accommodate other desired data.
  4. undo/redo problem
  5. Command management problem, command_id allocation problem
  6. Command call problem, good call syntax can simplify the burden on users

With these premises or constraints, the design of the command system will be more directional.

The strategies we have adopted are:

  1. Please see the previous series of articles memento pattern and the source code of undo-cxx.
  2. :)

I won’t repeat the excerpts in this article.

Tricks

protected virtual function

As a guideline for class writing, do not design public functions as virtual.

This criterion does not seem to be taken seriously.

But as an idiom and trick, virtual function is always protected, which is a kind of lingo among programmers: when you see a protected virtual function, derived classes know that it should be overloaded.

For the in-depth discussion of this criterion, this article is , you can listen to 16177450ba15ef Herb Sutter . are also related discussions in C++ FAQ 16177450ba15f7:

In the implementation of cmd_t, such guidelines are strictly followed. In my words, it is like this: A virtual function that can be overloaded represents a specific implementation and capability, so of course it should not be public, shouldn't it? If an interface must be public but allowed to be overloaded, it probably means that your design is not sufficiently split.

However, it does not necessarily have to be split: this idea also indirectly leads to another idiom, which is to split a virtual function that should be overloaded into ordinary member functions and virtual implementation functions:

class X {
  public:
  void chilling_out() { this->chilling_out_impl(); }
  
  protected:
  virtual void chilling_out_impl() = 0;
};

Is it unreasonable?

maybe.

private virtual function

BTW, to introduce a little knowledge that you may overlook, virtual functions can be set to private.

This sounds ridiculous, but it is true:

namespace {
  struct base { virtual ~base(){} };

  template<class T>
    struct base_t : public base {
      virtual T t() = 0;
      protected:
      void chilling_out() { this->chilling_out_impl(); }
      private:
      virtual void chilling_out_impl() = 0;
    };

  template<class T>
    struct A : public base_t<T> {
      A(){}
      A(T const& t_): _t(t_) {}
      ~A(){}
      T _t{};
      virtual T t() override { std::cout << _t << '\n'; return _t; }
      private:
      virtual void chilling_out_impl() override {}
    };
}

This code compiles and runs without any problems.

The virtual function is private, which means that the derived class cannot call it, but the base class itself can call it. Moreover, you can override it in derived classes. Not only that, you can even modify its access characteristics while overloading it in a derived class:

namespace {
  struct base { virtual ~base(){} };

  template<class T>
    struct base_t : public base {
      virtual T t() = 0;
      protected:
      void chilling_out() { this->chilling_out_impl(); }
      private:
      virtual void chilling_out_impl() = 0;
    };

  template<class T>
    struct A : public base_t<T> {
      A(){}
      A(T const& t_): _t(t_) {}
      ~A(){}
      T _t{};
      virtual T t() override { std::cout << _t << '\n'; return _t; }
      protected:
      // private:
      virtual void chilling_out_impl() override {}
    };

    struct B: public A<int> {
        virtual int t() override { std::cout << _t << '\n'; return _t; chilling_out_impl(); }
    };
}

As above, chilling_out_impl() is overloaded in struct A and changed to protected, so if you use it directly in derived class B, you won’t get an error. This is a useful feature. Sometimes we can conditionally overload certain details in other people's libraries; at the same time, this is also a useful bug. Does this kind of vulnerability really bring hidden dangers?

Refs

postscript

There are many techniques around virtual function, but in many cases, good design will allow you to earn money without any special skills at all.

That's great.

:end:


hedzr
95 声望19 粉丝