While the pattern is subtle, it is difficult to perfect, such as the question of the observer's life cycle in the observer pattern, such as the problem of circular dependencies in the visitor pattern, and so on, and many other modes such as the limited use of the scene, the complexity, the simplicity, the lack of universal, etc. But I think most of the deficiencies can be taken to make up for improvements, such as the use of c++11 new features to improve. Therefore, there is a c++11 to improve our pattern of this series. This time I'm going to talk about how to use c++11 to improve command mode. About Command mode
The function of the command mode is to encapsulate the request as an object, decouple the initiator and performer of the request, and support the queue and undo and redo of the request. Its class diagram is as follows:
Because the requests are encapsulated into a command object, we can centralize or defer processing of these command requests, and different client objects can share these commands, as well as control the priority of the request, queue, support request command undo, Redo, and so on. These benefits of command mode are obvious, but their problems are exposed during the actual use. As the request increases, the requested encapsulation class--the command class will also be more and more, especially in the GUI application, the request is very much. More and more command classes can cause class explosions and are difficult to manage. On the question of class explosions, gof early on, they propose a workaround: For simple commands that cannot be canceled and require no arguments, a command class template can be used to parameterize the receiver of the command, to parameterize the command class with the receiver type, and to maintain a binding between a receiver object and an action. This action is stored with a pointer to the same member function. The specific code is this:
Definition of a simple command class:
The constructor stores the behavior in the recipient and corresponding instance variables. The execute operation implements this action by the receiver.
To create a command object that invokes the action behavior on an instance of the MyClass class, only the following code is required:
Avoid creating new command classes with a generic simple command class. Is a good idea, however, this approach is not perfect, that is, it can only be a simple command class, can not be complex, or even all the command class generalization, which is its flaw, so it only partially solves the problem. I think I can improve this approach to defect, perfect solution to class explosion problems. I don't know if anyone has solved the problem before c++11, at least I haven't seen it. It is now possible to solve this problem perfectly by c++11.
c++11 Improved Command mode
The key to a perfect solution to the command-mode class explosion problem is how to define a generic, generalized command class that can generalize all the commands, rather than the simple commands mentioned by Gof. Let's go back to the simple command class definition in Gof, which simply generalizes the command class with no parameters and return values, and the command class internally references a receiver and receiver function pointer, and if the receiver's behavior function pointer has parameters, it cannot be generalized. So the key question we have to solve is how to get the command class to accept all the member function pointers or function objects.
Is there a class that accepts all member functions, normal functions, and function objects? Yes, in c++11, I mentioned a versatile function wrapper in my last blog, it can accept all function objects, fucntion, and Lamda expressions. No, because it's not perfect enough, it can't accept the member function yet, so it's not really a universal function wrapper. I'm going to expand it on the basis of it and make it a truly versatile function wrapper.
Wrappers that accept function, function objects, LAMDA, and normal functions:
Copy Code code as follows:
template< class F, class ... Args, class = TypeName std::enable_if<!std::is_member_function_pointer<f>::value>::type>
void Wrap (f && F, Args && ... Args)
{
Return f (std::forward<args> (Args) ...);
}
Wrapper to accept member functions:
Copy Code code as follows:
Template<class R, Class C, class ... Dargs, class P, class ... Args>
void Wrap (R (C::* f) (Dargs ...), p && p, Args && ... Args)
{
Return (*P.*F) (std::forward<args> (Args) ...);
}
The overloaded wrap allows it to receive member functions. Such a true sense of the omnipotent function wrapper is done. Now again, how it is applied to the command mode, the perfect solution to the problem of class explosion.
A generic generalization of the command class:
Copy Code code as follows:
Template<typename r=void>
struct Commcommand
{
Private
Std::function < R () > M_f;
Public
template< class F, class ... Args, class = TypeName std::enable_if<!std::is_member_function_pointer<f>::value>::type>
void Wrap (f && F, Args && ... Args)
{
M_f = [&]{return f (std::forward<args> (Args) ...);
}
Template<class R, Class C, class ... Dargs, class P, class ... Args>
void Wrap (R (C::* f) (Dargs ...) const, p && p, Args && ... Args)
{
M_f = [Ampersand, F]{return (*p.*f) (std::forward<args> (Args) ...);
}
Non-const member function
Template<class R, Class C, class ... Dargs, class P, class ... Args>
void Wrap (R (C::* f) (Dargs ...), p && p, Args && ... Args)
{
M_f = [Ampersand, F]{return (*p.*f) (std::forward<args> (Args) ...);
}
R Excecute ()
{
return M_f ();
}
};
Test code:
Copy Code code as follows:
struct STA
{
int m_a;
int operator () () {return m_a;}
int operator () (int n) {return m_a + N;}
int Triple0 () {return m_a * 3;}
int triple (int a) {return m_a * 3 + A;}
int Triple1 () const {return m_a * 3;}
const int Triple2 (int a) const {return m_a * 3+A;}
void Triple3 () {cout << "" <<endl;}
};
int add_one (int n)
{
return n + 1;
}
void Testwrap ()
{
commcommand<int> cmd;
Free function
Cmd. Wrap (add_one, 0);
Lambda function
Cmd. Wrap ([] (int n) {return n + 1;}, 1);
Functor
Cmd. Wrap (bloop);
Cmd. Wrap (Bloop, 4);
STA t = {10};
int x = 3;
member function
Cmd. Wrap (&STA::TRIPLE0, &t);
Cmd. Wrap (&sta::triple, &t, x);
Cmd. Wrap (&sta::triple, &t, 3);
Cmd. Wrap (&sta::triple2, &t, 3);
Auto R = cmd. Excecute ();
Commcommand<> cmd1;
Cmd1. Wrap (&bloop::triple3, &t);
Cmd1. Excecute ();
}
We define a universal function wrapper inside the generic command class so that we can encapsulate all the commands, add new requests without redefining the command, and perfectly solve the problem of command-class explosions.