设计模式基础
1 面向对象基本概念
想要学好设计模式这一课程,我们需要了解一些面向对象编程的一些基本概念。
- 封装(Encapsulation)
封装是面向对象编程的一个主要特征,它将对象的数据(属性)和操作这些数据的方法(行为)结合在一起,形成一个独立的对象。这样做的好处是可以隐藏内部的复杂性,只暴露有限的接口给外部使用。在Java中,通常通过设置成员变量为私有(private)来实现封装,并提供公共的方法(public methods)来访问和修改这些变量。
- 继承(Inheritance)
继承允许新创建的类(子类)继承现有类(父类)的属性和方法。这意味着可以在现有类的基础上构建新的类,从而重用和扩展现有代码。在Java中,继承是通过关键字extends来实现的。
- 多态(Polymorphism)
多态性是指允许不同类的对象对同一消息作出响应的能力。简单来说,就是允许使用父类类型的引用指向子类的对象。这样,同一个接口可以用来调用不同的实现方法,这些方法可能属于不同的类。
封装和继承的概念相对较为简单,多态相对来说比较复杂一点。
2 多态
C++多态(Polymorphism)允许使用基类指针或引用来调用子类的重写方法,从而使得同一接口可以表现不同的行为。
以下是多态的几个关键点:
虚函数(Virtual Functions):
- 在基类中声明一个函数为虚函数,使用关键字
virtual
。 - 派生类可以重写(override)这个虚函数。
- 调用虚函数时,会根据对象的实际类型来决定调用哪个版本的函数。
动态绑定(Dynamic Binding):
- 也称为晚期绑定(Late Binding),在运行时确定函数调用的具体实现。
- 需要使用指向基类的指针或引用来调用虚函数,编译器在运行时根据对象的实际类型来决定调用哪个函数。
纯虚函数(Pure Virtual Functions):
- 一个包含纯虚函数的类被称为抽象类(Abstract Class),它不能被直接实例化。
- 纯虚函数没有函数体,声明时使用
= 0
。 - 它强制派生类提供具体的实现。
多态的实现机制:
- 虚函数表(V-Table):C++运行时使用虚函数表来实现多态。每个包含虚函数的类都有一个虚函数表,表中存储了指向类中所有虚函数的指针。
- 虚函数指针(V-Ptr):对象中包含一个指向该类虚函数表的指针。
使用多态的优势:
- 代码复用:通过基类指针或引用,可以操作不同类型的派生类对象,实现代码的复用。
- 扩展性:新增派生类时,不需要修改依赖于基类的代码,只需要确保新类正确重写了虚函数。
- 解耦:多态允许程序设计更加模块化,降低类之间的耦合度。
注意事项:
- 只有通过基类的指针或引用调用虚函数时,才会发生多态。
- 如果直接使用派生类的对象调用函数,那么调用的是派生类中的版本,而不是基类中的版本。
- 多态性需要运行时类型信息(RTTI),这可能会增加程序的开销。
程序示例
Worker 基类:
cpp
class Worker
{
public:
Worker() = default;
virtual ~Worker() = default;
public:
virtual void DoWork() = 0;
};
Coder类继承自Worker类:
cpp
class Coder: public Worker
{
public:
Coder() = default;
~Coder() = default;
public:
void DoWork() override;
};
void Coder::DoWork()
{
qDebug() << "程序员写代码!";
}
Tester类继承自Worker类:
cpp
class Tester: public Worker
{
public:
Tester() = default;
~Tester() = default;
public:
void DoWork() override;
};
void Tester::DoWork()
{
qDebug() << "测试测试用例!";
}
测试函数:
cpp
void TestPolymorphism()
{
Worker* pWorker = nullptr;
pWorker = new Coder();
pWorker->DoWork(); // 调用Coder的DoWork方法
delete pWorker;
pWorker = nullptr;
pWorker = new Tester();
pWorker->DoWork(); // 调用Tester的DoWork方法
delete pWorker;
pWorker = nullptr;
}
有了多态的基础,我们还需要知道在面向对象编程中,我们类与类之间的一些常见关系,知道它们的UML类图怎么表示,学习设计模式本身不需要系统学习UML课程,但是基本的类图关系还是要会。
3 类间关系
设计模式中涉及到的常见的类之间的关系如下
- 接口实现关系
- 继承泛化关系
- 不可分离组合关系
- 可分离聚合关系
- 关联关系
- 依赖关系
接口实现关系
接口实现关系就是派生类必须重写接口中的所有方法,在UML类图中用"虚线+空心箭头"表示,其中箭头指向基类。
继承泛化关系
继承泛化关系就是常说的继承关系,派生类继承基类,基类被看作"一般设计",派生类被看作"特俗设计",因此继承泛化关系也被看作一般与特殊的关系,在UML类图中用"实线+实心箭头"表示,其中箭头指向基类。

不可分离组合关系
不可分离组合关系可以用整体和部分之间的关系来解释,部分是不能脱离整体单独存在的。部分对象与整体对象是不可分离的,一旦整体对象析构,部分对象就会随之消失,它们属于同一个生命周期。不可分离组合关系在UML类中用"实线+实心菱形"表示,其中实心菱形指向整体。

可分离聚合关系
可分离聚合关系也可以说成是整体与部分的关系,它与不可分离组合关系的区别是,这种整体与部分是可以分离的,也就是说部分是可以脱离整体单独存在的。UML类中用"实线+空心菱形"表示,其中空心菱形指向整体。

关联关系
关联关系就是一个类与另一个类在对象之间的联系,联系可以是双向的,也可以是单向的。在UML类图中,双向关联关系用没有箭头的实线表示,单向关联关系用"实线+箭头"表示,箭头指向被关联的类。
依赖关系
只有在一个类依赖另一个类中的方法时才存在依赖关系,一般将类作为参数传递,通过对方法的调用实现一个类访问另一个类的功能。在UML类图中,使用带箭头的虚线表示这类关系,箭头指向被依赖的类。