GOTW #14 Class Relationships part i<?xml:namespace prefix = o ns = "Urn:schemas-microsoft-com:office:office"/>
Author: Herb Sutter
Translation: Kingofark
[Disclaimer]: The content of this article is taken from the Guru of the week column on the www.gotw.ca website, and its copyright belongs to the original person. The translator Kingofark to translate this article without the consent of the original person. This translation content is only for self-study and reference, please all read this article do not reprint, dissemination of this translation content; Anyone who downloads this translation should delete its backup immediately after reading the browsing. The translator Kingofark is not liable to those who violate the two principles. Hereby DECLARE.
Revision 1.0
Guru of the Week Terms - : Relationships between classes (last article)
Difficulty: 5/10
(What is your object-oriented design level?) This article will describe a mistake that so far has been made by many programmers--well, of course, about the design of the class.
[ question]
Network applications typically have two different communication sessions (communications session), each communication session (communication sessions) has its own specific message protocol (Messages protocol). Of course, the two protocols also have similarities, such as some operations and some messages (message) may be the same. Thus, when programmers want to encapsulate some operations and messages in the Basicprotocol class, it is possible to design the following:
Class Basicprotocol/*: Possible base Classes/*
Public
Basicprotocol ();
Virtual ~basicprotocol ();
BOOL Basicmsga (/*...*/);
BOOL Basicmsgb (/*...*/);
BOOL BASICMSGC (/*...*/);
};
Class Protocol1:public Basicprotocol {
Public
Protocol1 ();
~protocol1 ();
BOOL DoMsg1 (/*...*/);
BOOL DoMsg2 (/*...*/);
BOOL DoMsg3 (/*...*/);
BOOL DoMsg4 (/*...*/);
};
Class Protocol2:public Basicprotocol {
Public
Protocol2 ();
~protocol2 ();
BOOL DoMsg1 (/*...*/);
BOOL DoMsg2 (/*...*/);
BOOL DoMsg3 (/*...*/);
BOOL DoMsg4 (/*...*/);
BOOL DoMsg5 (/*...*/);
};
member functions in code can perform necessary operations by dropping functions of the base class, but the real transport operations (transmission) are implemented by themselves. Each class in the code, of course, may also contain other members. Here we assume that all the members associated with the problem are already listed in the code we see.
Try to analyze this design in your code. Is there anything that needs to be changed? If so, explain why it needs to be modified.
[ answer]
This article points out a common hidden danger in the design of "relationships between classes". Let's recall the code given above: Protocol1 and Protocol2 These two classes derive from the base class Basicprotocol in public, and the base class Basicprotocol accomplish certain tasks.
The key to this question is the following sentence:
member functions in code can perform necessary operations by dropping functions of the base class, but the real transport operations (transmission) are implemented by themselves.
Here's the problem: The code clearly depicts a "is implemented in terms" (implemented by something) relationship. [1] in C + +, this relationship means implementing private inheritance (private inheritance) or "membership (Member attribution)". Unfortunately, many people have been thinking that this relationship means implementing the "public Inheritance (publicly inherited)". They confused implementation inheritance (Implementation inheritance) with interface Inheritance (interface inheritance). In fact, implementation inheritance (Implementation inheritance) and interface Inheritance (interface inheritance) are completely different, and people's confusion about this stems from the issues we are discussing in this article. [2] [Note 1]
The following clues explain the problem here in more detail:
1. The Basicprotocol class does not provide a virtual function (which does not include that destructor; the problem with the destructor is mentioned in the following description). [Note 2] This means that it is not intended to be used in a polymorphically (polymorphic) manner, and this implies that the public inheritance (inheritance) mechanism should not be used.
2. There are no protected functions or protected members in the Basicprotocol class. This means that there is no "derivation interface (derived interface)" In the class, which means that basicprotocol should not be inherited in any way-either public or private.
3. Basicprotocol encapsulates certain operations, but it is not as capable of implementing its own transport operations as its derived classes. This means that the Basicprotocol object is not as useful as the object of its derived class (WORKS-LIKE-A), as well as the object of its derived protocol class (USABLE-AS-A). Public inheritance (inheritance) should only model (model) one (and only) relationship: a real interface is-a (is a) relationship, which follows Liskov substitution Principle (Riskov substitution principle). [3] For the sake of clarity, I usually refer to it as works-like-a (work like something) and usable-as-a (as useful as something). [Note 3]
4. These derived classes only use the Basicprotocol public Interface (interface). This means that they do not derive any additional benefits from Basicprotocol. We can fully use an auxiliary Basicprotocol object to accomplish the tasks they undertake.
As a result, we need to make some changes to the code:
First of all, since the design is not intended to allow Basicprotocol to be inherited by other classes as a base class, its virtual functions are unnecessary and should be removed. Secondly, Basicprotocol should be changed into a name such as Messagecreator, so that it does not mislead others, resulting in misunderstanding.
So, after the above changes, which way should be used to mold (model) "is implemented in terms of" (according to the implementation) of this relationship. In private inheritance (privately inherited) or membership (Member attribution) mode.
The answer to this question is easy to remember:
[Learning Guide]: The membership (Member attribution) method is always used in the relationship of moulding (model) "is implemented in terms" (implemented according to something). Private inheritance is only used when there is a need to form an inheritance relationship-that is, when you need to be able to access protected members, or when you need to overwrite (override) virtual functions. Never use the public inheritance (publicly inherited) method simply because you want to reuse code.
The use of membership (member attribution), so that we can better separate some of the problems to consider, because this time the user and so on (using Class) became a common client, only to the user (used class) of the public interface access. So, use the membership (Member attribution) approach, so you'll find that your code is much simpler, more readable, and easier to maintain. To put it simply, your code will save a lot of money in terms of cost.
*********************************************
[Note 1]: As it happens, those who are prone to making this mistake often generate very deep inheritance hierarchies. This greatly increases the burden of maintenance on the following two reasons:
• Increased unnecessary complexity
L Force users to understand a lot of unnecessary class interfaces, even if they just want to use a particular derived class.
In addition, because of the increased unnecessary vtable and the indirection of classes that do not require the use of vtable, this has an impact on memory usage and program performance. If you find yourself often generating deeper levels of inheritance, you should look at your design and see if you have developed this bad habit. In fact, the deeper level system is rarely used, even basically is not a good thing ... If you don't believe, and still think, "If you don't have a lot of inheritance, you can't be an object oriented," then you should look at the standard library--that's a good example of what you think is wrong.
[Note 2]: Even if the basicprotocol itself derives from another class, the conclusion here is invariant, since Basicprotocol does not provide any new virtual functions. If there is a basicprotocol base class that really provides a virtual function, it simply means that the base class wants to be used in a polymorphically (polymorphic) way rather than basicprotocol So we should at most be inheriting from that base class (rather than inheriting from Basicprotocol).
[Note 3]: It is true that when you inherit a public way to get an interface, if the base class has both the interfaces you need and some specific implementations, then some of these implementations will come with you. This effect can almost always be avoided by design, but it is not always necessary to take a particularly pure "one responsibility per class (a single responsibility)" approach.
[1]: For "is implemented in terms of", see Scott Meyers "effective C + +" clause 40 and clause 42.
[2]: see Scott Meyers, effective C + +, clause 36: Distinguishing interface Inheritance (interface inheritance) and implementation inheritance (implementing inheritance).
[3]: Regarding the Liskov substitution principle (Riskov substitution principle), please refer to the discussion in article 35 of Scott Houtie, "Meyers C + +", as translated by Mr. Effective.
Finish