C++设计模式:类间关系
类封装了数据和行为,是面向对象的重要组成部分,它是具有相同属性、操作、关系的对象集合的总称。在系统中,每个类都具有一定的职责,职责指的是类要完成什么样子的功能,要承担什么样子的义务。一个类可以有多种职责,但是设计得好的类一般只有一种职责。
类与类之间的关系
继承关系
继承也叫作泛化(Generalization),子类(派生类)继承父类(基类)的属性和方法,并可扩展或重写父类功能。
C++ 中没有专门的 “接口” 关键字,通常用纯虚基类模拟接口,子类通过 public
继承该纯虚类并实现其所有纯虚函数。
class Bird
{
public:string getName(){return m_name;}void setName(string name){m_name = name;}virtual void fly() {}virtual void eat() {}
protected:string m_sex;string m_name;
};class Cuckoo : public Bird
{
public:void fly() override{cout << "我拍打翅膀飞行..." << endl;}void eat() override{cout << "我喜欢吃肉肉的小虫子..." << endl;}
};class Eagle : public Bird
{
public:void fly() override{cout << "我展翅翱翔..." << endl;}void eat() override{cout << "我喜欢吃小动物..." << endl;}
};
组合关系
组合(Composition)表示 “整体 - 部分” 关系,部分完全依赖整体存在(强包含)。
部分不能脱离整体独立存在,整体的生命周期决定部分的生命周期(整体创建时部分被创建,整体销毁时部分也被销毁),部分只能属于一个整体。
例如人和心脏:心脏是人的一部分,人死亡后心脏也不再独立存在。
class Heart {
public:void beat() {}
};
class Person {
private:Heart heart; // 组合关系(部分作为成员变量,随整体创建/销毁)
public:Person() : heart() {} // 构造时创建心脏
};
聚合关系
聚合(Aggregation)表示 “整体 - 部分” 关系,部分可脱离整体独立存在(弱包含)。
整体通过成员变量持有部分的指针 / 引用,整体包含部分,但部分的生命周期不由整体控制,部分可属于多个整体。
比如公司和员工:员工是公司的一部分,但员工可离职(脱离公司存在)。
class Employee {};
class Company {
private:std::vector<Employee*> employees; // 聚合员工(部分)
public:void addEmployee(Employee* e) {employees.push_back(e);}
};
关联关系
表示两个类之间存在长期、稳定的连接,通常体现为一个类持有另一个类的对象引用(或指针)。
关系可单向或双向(如 A 关联 B,或 A 与 B 相互关联),类之间生命周期相互独立,关联不影响对象的创建与销毁。
单向关联
单向关联指的是关联只有一个方向,比如每个孩子都拥有一个Parent,其代码实现为:
class Parent
{
};class Child
{
private:Parent m_father;
};
双向关联
现实生活中每个孩子都有父母,每个父母同样有自己的孩子,如果想要通过类来描述这样的亲情关系,代码如下:
class Parent
{
private:Child* m_son;
};class Child
{
private:Parent* m_father;
};
自关联
自关联指的就是当前类中包含一个自身类型的对象成员,这在链表中非常常见,单向链表中都会有一个指向自身节点类型的后继指针成员,而双向链表中会包含一个指向自身节点类型的前驱指针和一个指向自身节点类型的后继指针。就以双向链表节点类为例,它的C++写法为:
class Node
{
private:void* m_data;Node* m_prev;Node* m_next;
};
依赖关系
表示一个类(依赖方)在执行特定操作时,临时需要另一个类(被依赖方)的协助,是一种短期的 “使用” 关系。
通常通过方法参数、局部变量或静态方法调用体现,无长期持有关系,操作结束后依赖消失。
例如计算器计算时需要临时使用数字Number
对象:
class Number {
public:int value;
};
class Calculator {
public:// 通过参数依赖 Numberint add(Number a, Number b) { return a.value + b.value; }
};
总结
类之间的关系强弱顺序是这样的:继承(含接口实现) > 组合 > 聚合 > 关联 > 依赖。
在实际设计中,应优先使用低耦合关系(如关联、聚合、组合),避免过度依赖继承,以提高代码的灵活性和可维护性。