Use experience 91 to distinguish inheritance, templates, and combinations

As a C + + programming developer, you can consider the following three design problems:

(1) Design a class that describes queues. You may need different classes because each queue processes different data. For example, members may have a queue representing int, a queue representing string, or even a queue representing string queue, etc. However, if you are not required to use the standard STL library, suppose that the queue type to be designed needs to have the following operation methods: create a queue, destroy the queue, add the object to the tail of the queue, obtain the object from the head, and check whether the queue is empty. How should you design it?

(2) Design a type descriptor. Conceivably, this may require different classes, or even create people's queues. Each type of person has different behaviors, such as students have to study, workers have to work and so on. But there is one thing in common: people can create and destroy.

(3) We consider the human head. Eyes, nose, mouth and ears are all components of the head. Eyes can see, nose can smell, mouth can eat, ears can listen. So how do you design it?

These three problems are quite similar, but they are three completely different design concepts. In the subsequent part of this practical experience, we will deeply explore the differences between the three.

Best practices

  • For queues and people, there are different types to deal with (queues contain objects of type T, while people are of type T), but you must ask yourself this question: does type t affect the behavior of classes? If t does not affect behavior, you can use templates. If t affects behavior, you need virtual functions and use inheritance.
  • For people and heads, we have to deal with different behaviors. However, you must clearly know the relationship between different classes. If the relationship between classes is a kind of relationship, please select inheritance relationship. If a part of relationship is displayed, please select a combination relationship.

The following is the declared implementation of the queue template.

// Queue template implementation.
template <class T> 
class CQueue
{ 
	//Define the node structure of the queue 
	struct NODE 
	{ 
	NODE<T>* next; 
	T data; 
	};

public: 
	CQueue();
	virtual ~ CQueue();

	//Join the team at the end of the team 
	void push(T e) 
	//Get out of the team at the head of the team 
	T pop();
	//Determine whether the queue is empty 
	bool empty(); 
private: 
	//Pointer to the header node. Front - > next - > data is the first element of the team head.
	NODE<T>*m_front; 
	//Pointer to the end of the queue (the last element added)
	NODE<T>* m_rear;  
}; 

The queue is implemented, but how can humans achieve it? Why is human class implementation not suitable for templates?
As mentioned earlier, people have one thing in common: people can create and destroy. ". This means that different behavior implementations must be provided for each kind. You can't write a function to handle the behavior of all types of people. We can customize a function interface so that all kinds of people can implement it. This seems to be inheritance. Yes, it is indeed inheritance. Moreover, the function interface must be declared as a pure virtual function.

class CPerson
 {
public:
	virtual ~ CPerson ();
	virtual void Work() = 0; 
};

Human subclasses, such as Student and Worker, of course have to redefine the inherited Work function interface:

// Student class implementation
class CStudent: public CPerson
{
public:
	virtual void Work();
};
// Worker realization
class CWorker: public CPerson
{
public:
	virtual void Work();
};

So far, we know that the template is suitable for the CQueue class and the inheritance is suitable for the CPerson class. The only remaining question is why inheritance is not suitable for the CQueue class. To solve this problem, try declaring a CQueue queue base class from which all other queue classes inherit:

class CQueue
{ 
public: 
	CQueue();
	virtual ~ CQueue();
	//Join the team at the end of the team 
	void push(const ??? e) 
	//Get out of the team at the head of the team 
	T pop();
	//Determine whether the queue is empty 
	bool empty(); 
private: 
	//Pointer to the header node. Front - > next - > data is the first element of the team head.
	NODE<T>* front; 
	//Pointer to the end of the queue (the last element added) 
	NODE<T>* rear;  
};

It is obvious that the input parameter type of the push function cannot be determined. So this implementation is problematic.

After understanding the relationship between inheritance and template, we continue to discuss inheritance and composition. The relationship between the two types of inheritance description we discussed above is kind of. The combination describes the relationship of a part of.

The combination mode is implemented. The Eye, Nose, Mouth and Ear are part of the Head, so the class Head should be composed of Eye, Nose, Mouth and Ear, not derived.

// Eye class
class Eye
{
public:
  void Look(void);
};
// Nose class
class Nose
{
public:
    void Smell(void);
};
// Mouth class
class Mouth
{
public:
  void Eat(void);
};
// Ear class
class Ear
{
public:
  void Listen(void);
};
// Implementation of header class.
class Head
{
public:
	void Look(void)
 	{
 		m_eye.Look();
    }
    void Smell(void)
 	{ 
		m_nose.Smell(); 
	}
	void Eat(void)
 	{
 		m_mouth.Eat();
    }
    void Listen(void)
 	{
    	m_ear.Listen();
 	}
private:
  Eye m_eye;
  Nose m_nose;
  Mouth m_mouth;
   Ear m_ear;
};

If the Head is allowed to derive from Eye, Nose, Mouth and Ear, the Head will automatically have the functions of Look, smile, Eat and Listen:

class Head : public Eye, public Nose, public Mouth, public Ear
{
};

The above program is very short and runs correctly, but this design is wrong. Head is not Eye, not Nose. This does not conform to the basic principle of public inheritance. Although it works correctly here, it is not guaranteed to work correctly in all cases.

summary

  • When the type of the object does not affect the behavior of the functions in the class, it is necessary to use the template to generate such a set of classes.
  • When the type of an object affects the behavior of functions in a class, inheritance is used to get such a set of classes.
  • Select inheritance when the relationship between the object and the object is kind of, and select combination when it is a part of.

Please remember

  • Distinguish between template and inheritance. When the type of object does not affect the behavior of functions in the class, choose to use template. When the type of object affects the behavior of functions in the class, choose inheritance instead of template.
  • Distinguish between inheritance and combination. Select inheritance if the object relationship is kind of relationship, and select combination if it is a part of relationship.

Keywords: OOP

Added by ryanwood4 on Thu, 21 Oct 2021 17:50:30 +0300