1.1 background
The current XXXX extension interface is defined as follows:
Class ProductProcesser
{
PUBLIC:
Virtual Long Process (const in_type &, out_type &) = 0;
.... // There are other expansion interfaces to be defined together.
}
Discuss the definition of the above process member function. Because of the change in product requirements, the current process function needs to be called in both cases, so consider two modifications:
1) Modify Process as: Long (int Flag, Const in_type &, out_type&)
FLAG represents different applications
2) Define virtual functions of different names
3) Modify Process to Long Process (int FLAG, VOID * IN_PDATA, VOID * OUT_PDATA) = 0; this way is intended to support different processing parameters
We all hope that the system has better scalability after this change.
1.3 analysis
First, each design has its own advantages and disadvantages, there is no best solution. It is only possible to analyze if a design has improved in accordance with some basic principles of software design.
1. See the definition of the interface productProcess, because the extension interfaces containing multiple processing services are basically no relationship, which is loosely coupled, which is clearly contrary to ISP (interface separation principles).
2. Long (int flag, const in_type &, out_type ", first requiring platform and product to be consistent on the processing of the Flag parameter, but just verbal or document agreed, unable to avoid inconsistency in the actual code, resulting in platform and product code Tight coupling. In addition, it is required to implement in the same interface for various business processing processes. Once a business interface changes, the implementation of other business interfaces will affect other business interfaces, and individuals think it is obvious to OCP (open closure principles)
3. Long Process (int flag, void * in_pdata, void * out_pdata), wants to implement an extended service processing interface, and the type of business processing data is not limited, but it is actually not done because a business processing interface may There are 1 parameters, and there may be multiple parameters. For the latter, it is also necessary to pack multiple parameters. It is inconvenient to use. The key is to use VOID * to transmit various data is not type security, C should be used with caution.
1.4 consider
It is possible to use a better way to implement the design intent of long process (int flag, void * in_pdata, void * out_pdata), even its adaptation range, that is, the number of parameters and types are not limited. Of course, it is achieved with an object-oriented ideological way, and it is achieved by techniques such as interfaces, inheritance. It seems that it is more complicated than a simple member function, but more flexible, even unified expansion interfaces.
1.5 My implementation
Because the number of parameters and parameters of each business process are different, all business processing cannot be defined by an interface, considering the implementation of a degraded interface type, as follows:
ExpandInterface is a base class definition for any extended interface, just providing a type indicating that the specific service processing interface is defined by its sub-interface definition, and the specific implementation of the secondary developer is the subclass of these sub-interfaces. For example, the above is a synchronous alarm and a real-time alarm define different sub-interfaces. These two sub-interfaces look like a touch, why not unify? I think this is just the interface definition, and the repetition code can be ignored; their behavior is different, and the different interface names are very easy to understand. Such code can be said to be self-comment; the two alarm business processing is not related, separate It can then be independently changed no effect on the other. From the above three aspects, the separation definition is better. In addition, the platform should provide an interface for the secondary developer to set its implementation classes, providing such an expansion interface management class:
ExpandManager is an implementation class provided by the platform, and the secondary developer can use directly, it can only see the ExpandInterface interface.
For platform developers, there is a problem, how do it know that an expandinterface is REAALALMINTERFACE or SYNALMINTERFACE? Because ExpandManager, ExpandInterface, REAALMINTERFACE, SYNALMINTERFACE, Synalminterface is a platform definition and implementation, from the technical report, platform developers can use Dynamic_cast to convert each ExpandInterface to try, and finally find their own interface.
Unfortunately, we cannot use TypeID to implement expandManager :: add (expandinterface *):
Void add (expandinterface * p) {
_MAP [typeid (* p) .Name ()] = p; // Directly use TypeID to use TypeID directly to a real subclass type
}
We cannot get the type information of the intermediate level (the REAALMINTERFACE) of the intermediate level (here).
In order to avoid frequent use of Dynamic_cast, we need to increase the ability to determine the child interface type on an ExpandInterface interface, which is easy to think of:
Class ExpandInterface
{
PUBLIC:
Virtual int getProtocol () = 0;
}
Class ReaLalMinterface: Public ExpandInterface
{
Virtual int getProtocl () {return real_alm_processor;}
}
The platform is required to define each business processing interface, which is also the subclass of ExpandInterface implementing getProtocol pure virtual interface, so that the implementation of ExpandManager :: Add (ExpandInterface *) is very simple:
Void add (expandinterface * p) {_map [p-> getprotocl ()] = p;}
By this way, the platform developers are also easy to obtain a business handling interface. The new service handling interface only needs to ensure that its business identifier is unique when the platform is defined, and no repetition can, and no secondary development. relationship.
However, this method has indeed a relatively large disadvantage that the secondary developer has also overloaded the GetProtoCL interface and returns different values, which may be malicious, or unintentional. To avoid this, we need to provide documentation explanation that the secondary developer does not overload this interface, but cannot guarantee what will happen in the actual code.
We need an interface defined in an ExpandInterface, the platform can be used, can be modified, the product cannot be used, and the Visitor mode can help us do this. The business type is also the type information of the subclass of each ExpandInterface saved in the Visitor class. The key to secondary developers cannot modify the RelatMinterface :: Accept interface is that the platform is not an external definition of the Visitor class, just in the ExpandInterface definition file, using the Visitor class's forward statement, such two developers can do more The thing is to implement an empty Accept function, so that when the platform is implemented, the Visitor is assigned a reasonable default value before calling Accept, which can detect this situation, give the rejection or warning prompt, in summathe, all are on the platform Under the control.
1.6 full class diagram
External interface:
ExpandInterface, Relanterface, Synalminterface
External implementation, direct use:
ExpandManager
Only the definition provided within:
Visitor
Secondary development needs:
ConcretRealalM, Concretsynalm
1.7 other considerations
Some business allows multiple extension interface serial processing, which is supported in this way is not recommended to change in the ExpandManager and ExpandInterface level, but to design the subclass of ExpandInterface, the service processing interface design as a criminal pattern (chain of responsibility), At the same time, the template method can be combined:
This universal interface design can be used in any place where the extension is required, and the platform and secondary developers only need to interact with any ambiguous interface, and the secondary developer is unintentionally or malicious.