【目录】
设计模式学习之路1----设计模式的三大分类及六大原则
设计模式学习之路2----创建型模式详解
设计模式学习之路3----结构型模式详解上
设计模式学习之路4----结构型模式详解下
设计模式学习之路5----行为型模式详解上
设计模式学习之路6----行为型模式详解下
本博客专门介绍创建型模式
1、简单工厂模式
工厂模式提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
实例
我们先创建一个表示动物通用特性的接口
class Animal{
public:
//虚函数
virtual void run();
};
再随便创建两个实现类
class Cat:public Animal{
public:
void run()
{
printf("Cat\n");
}
};
class Dog:public Animal{
public:
void run()
{
printf("Dog\n");
}
};
最后我们测试一下工厂类的作用
【代码】
//组合
#include<bits/stdc++.h>
using namespace std;
//接口
class Animal{
public:
//纯虚函数,不用纯虚函数编译会报错
virtual void run() = 0;
};
class Cat:public Animal{
public:
void run()
{
printf("Cat\n");
}
};
class Dog:public Animal{
public:
void run()
{
printf("Dog\n");
}
};
class AnimalFactory{
public:
static Animal *create(string name)
{
if(name=="Cat"){
return new Cat();
}
if(name=="Dog"){
return new Dog();
}
return NULL;
}
};
int main(){
Animal *cat = AnimalFactory::create("Cat");
cat->run();
Animal *dog = AnimalFactory::create("Dog");
dog->run();
Animal *other = AnimalFactory::create("sdg");
other->run();
}
【运行结果】
2、工厂方法模式(Factory Method)
简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。示例
先创建一个动物接口
class Animal{
public:
//纯虚函数
virtual void run() = 0;
};
随便创建两个实现类
class Cat:public Animal{
public:
void run()
{
printf("Cat\n");
}
};
class Dog:public Animal{
public:
void run()
{
printf("Dog\n");
}
};
再提供一个工厂接口
class Factory{
public:
virtual Animal *CreateAnimal() = 0;
};
提供两个工厂类
class CatFactory:public Factory{
public:
Animal *CreateAnimal(){
return new Cat();
}
};
class DogFactory:public Factory{
public:
Animal *CreateAnimal(){
return new Dog();
}
};
【测试代码】
//组合
#include<bits/stdc++.h>
using namespace std;
//接口
class Animal{
public:
//纯虚函数
virtual void run() = 0;
};
class Cat:public Animal{
public:
void run()
{
printf("Cat\n");
}
};
class Dog:public Animal{
public:
void run()
{
printf("Dog\n");
}
};
class Factory{
public:
virtual Animal *CreateAnimal() = 0;
};
class CatFactory:public Factory{
public:
Animal *CreateAnimal(){
return new Cat();
}
};
class DogFactory:public Factory{
public:
Animal *CreateAnimal(){
return new Dog();
}
};
int main(){
//创建一个猫厂
CatFactory *catFactory = new CatFactory();
//创建一只猫
Animal *cat = catFactory->CreateAnimal();
cat->run();
//创建一个够厂
DogFactory *dogFactory = new DogFactory();
//创建一条狗
Animal *dog = dogFactory->CreateAnimal();
dog->run();
}
【测试结果】
【总结】
这个模式的最大的好处在于不必修改现有的代码,拓展性较好!但是可能需要的类较多,使程序变得臃肿。
3、抽象工厂模式(Abstract Factory Pattern)
与工厂方法的区别:工厂方法可以说是抽象工厂的一个特例,当工厂里只有一种产品的时候,抽象工厂的结构就简化成工厂方法的结构了。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。我们换一种容易理解的方式来讲解抽象工厂模式,例如我们需要键盘和鼠标来组装电脑,华为电脑需要配置华为键盘和华为鼠标,小米电脑需要配置小米键盘和小米鼠标。那么使用抽象工厂模式,在为电脑生产相关配件时,就无需制定配件的型号,它会自动根据电脑类型生产对应的配件。
注意事项
产品族难扩展,产品等级易扩展。例子
QQ 换皮肤,一整套一起换。示例
创建鼠标类
//鼠标类
class Mouse{
};
//华为鼠标类
class HuaWeiMouse{
public:
void say()
{
printf("这是一个华为鼠标类\n");
}
};
//小米鼠标类
class XiaoMiMouse{
public:
void say()
{
printf("这是一个小米鼠标类\n");
}
};
创建键盘类
//键盘类
class KeyBoard{
};
//华为键盘类
class HuaWeiKeyBoard:public KeyBoard{
public:
void say()
{
printf("这是一个华为键盘类\n");
}
};
//小米鼠标类
class XiaoMiKeyBoard:public KeyBoard{
public:
void say()
{
printf("这是一个小米键盘类\n");
}
};
创建电脑工厂接口
//创建一个抽象工厂类
class AbstractFactory{
public:
virtual Mouse *CreateMouse() = 0;
virtual KeyBoard *CreateKeyBoar() = 0;
};
然后创建华为电脑、小米电脑
//创建华为电脑工厂
class HuaWeiComputer:public AbstractFactory{
public:
Mouse *CreateMouse(){
return new HuaWeiMouse();
}
KeyBoard *CreateKeyBoar(){
return new HuaWeiKeyBoard();
}
};
//创建小米电脑工厂
class XiaoMiComputer:public AbstractFactory{
public:
Mouse *CreateMouse(){
return new XiaoMiMouse();
}
KeyBoard *CreateKeyBoar(){
return new XiaoMiKeyBoard();
}
};
【完整代码测试】
//组合
#include<bits/stdc++.h>
using namespace std;
//鼠标类
class Mouse{
};
//华为鼠标类
class HuaWeiMouse:public Mouse{
public:
void say()
{
printf("这是一个华为鼠标类\n");
}
};
//小米鼠标类
class XiaoMiMouse:public Mouse{
public:
void say()
{
printf("这是一个小米鼠标类\n");
}
};
//键盘类
class KeyBoard{
};
//华为键盘类
class HuaWeiKeyBoard:public KeyBoard{
public:
void say()
{
printf("这是一个华为键盘类\n");
}
};
//小米鼠标类
class XiaoMiKeyBoard:public KeyBoard{
public:
void say()
{
printf("这是一个小米键盘类\n");
}
};
//创建一个抽象工厂类
class AbstractFactory{
public:
virtual Mouse *CreateMouse() = 0;
virtual KeyBoard *CreateKeyBoar() = 0;
};
//创建华为电脑工厂
class HuaWeiComputer:public AbstractFactory{
public:
Mouse *CreateMouse(){
return new HuaWeiMouse();
}
KeyBoard *CreateKeyBoar(){
return new HuaWeiKeyBoard();
}
};
//创建小米电脑工厂
class XiaoMiComputer:public AbstractFactory{
public:
Mouse *CreateMouse(){
return new XiaoMiMouse();
}
KeyBoard *CreateKeyBoar(){
return new XiaoMiKeyBoard();
}
};
int main(){
//创建一个华为工厂
HuaWeiComputer* huawei = new HuaWeiComputer();
//这里不同于java,需要强制转换
HuaWeiMouse* huaweimouse = (HuaWeiMouse*)huawei->CreateMouse();
huaweimouse->say();
}
【测试结果】
与工厂方法的区别:工厂方法可以说是抽象工厂的一个特例,当工厂里只有一种产品的时候,抽象工厂的结构就简化成工厂方法的结构了。
比如这个例子,如果只有华为,那么就是工厂方法了
4、单例模式
顾名思义,单例模式确保只有单个对象被创建
下面介绍实现单例模式的几种方式
线程不安全的懒汉式
这种方式是最基本的实现方式,且lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
【懒汉式】
这种方式是最基本的实现方式,不要求线程安全,在多线程不能正常工作。懒汉式的特点是延迟加载,比如配置文件,采用懒汉式的方法,配置文件的实例直到用到的时候才会加载
//懒汉式
#include<bits/stdc++.h>
using namespace std;
class Singleton
{
private:
static Singleton *instance;
Singleton(){}
public:
static Singleton* getInstance(){
if(instance == NULL){
instance = new Singleton();
}
return instance;
}
};
//不加这句编译不通过
Singleton*Singleton::instance = NULL;
int main(){
Singleton *it = Singleton::getInstance();
}
【线程安全的懒汉式】
用了互斥量mutex
//线程懒汉式
#include<bits/stdc++.h>
using namespace std;
mutex it;
class Singleton
{
private:
static Singleton *instance;
Singleton(){}
public:
static Singleton* getInstance(){
it.lock();
if(instance == NULL){
instance = new Singleton();
}
it.unlock();
return instance;
}
};
//不加这句编译不通过
Singleton*Singleton::instance = NULL;
int main(){
Singleton *it = Singleton::getInstance();
}
【饿汉式】
没有 Lazy 初始化,容易产生垃圾对象。多线程安全,没有加锁,执行效率会提高。
//饿汉式
#include<bits/stdc++.h>
using namespace std;
mutex it;
class Singleton
{
private:
static Singleton *instance;
Singleton(){}
public:
static Singleton* getInstance(){
return instance;
}
void print()
{
cout << "this = " << this << endl;
}
};
//与懒汉式差别的地方
Singleton* Singleton::instance = new Singleton();
int main(){
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
s1->print();
s2->print();
}
双检锁/双重校验锁(DCL,即 double-checked locking)
登记式/静态内部类
这两类就没有深入去了解了
5、建造者模式
无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车,可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。建造者模式(Builder Pattern)可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。它一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
优点
(1 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
(2 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,
(3 用户使用不同的具体建造者即可得到不同的产品对象 。
(4 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
( 5 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合 “开闭原则”
缺点
(1 产品之间差异性很大的情况:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
(2 产品内部变化很复杂的情况: 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
抽象工厂模式与建造者模式的区别
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。示例
我们先建造一个具体的产品对象 – 单车
//自行车的产品对象
class BikeProduct
{
private:
//轮胎
string tyre;
//座椅
string seat;
public:
BikeProduct(){}
string getTyre(){
return tyre;
}
string getSeat(){
return seat;
}
void setTyre(string a){
tyre = a;
}
void setSeat(string a){
seat = a;
}
};
接着,我们构建一个抽象建造者Builder,用于创建产品对象的各个指定部件
//自行车建造者抽象类
class BikeBuilder{
public:
BikeProduct *bike = new BikeProduct();
virtual void buildTyre()=0;
virtual void buildSeat()=0;
BikeProduct* getBike(){
return bike;
}
};
然后我们构建具体建造者,用于构建和装配各个部件
//摩拜单车 具体构建者
class Mobiebuilder:public BikeBuilder
{
public:
void buildTyre(){
bike->setTyre("摩拜轮胎");
}
void buildSeat(){
bike->setSeat("摩拜座位");
}
};
//ofo单车 具体构建者
class Ofobuilder:public BikeBuilder
{
public:
void buildTyre(){
bike->setTyre("ofo轮胎");
}
void buildSeat(){
bike->setSeat("ofo座位");
}
};
接着我们构建一个指挥者,使用Builder接口类装配对象。它不仅可以隔离了客户与对象的生产过程,也负责控制产品对象的生产过程。
//指挥者
class Director{
private:
BikeBuilder* bikeBuilder;
public:
void setBikeBuilder(BikeBuilder *a){
bikeBuilder = a;
}
//组装
BikeProduct* construct(){
bikeBuilder->buildTyre();
bikeBuilder->buildSeat();
return bikeBuilder->bike;
}
};
【完整测试代码】
//建造者模式
#include<bits/stdc++.h>
using namespace std;
//自行车的产品对象
class BikeProduct
{
private:
//轮胎
string tyre;
//座椅
string seat;
public:
BikeProduct(){}
string getTyre(){
return tyre;
}
string getSeat(){
return seat;
}
void setTyre(string a){
tyre = a;
}
void setSeat(string a){
seat = a;
}
};
//自行车建造者抽象类
class BikeBuilder{
public:
BikeProduct *bike = new BikeProduct();
virtual void buildTyre()=0;
virtual void buildSeat()=0;
BikeProduct* getBike(){
return bike;
}
};
//BikeBuilder* BikeBuilder::bike = new BikeBuilder();
//摩拜单车 具体构建者
class Mobiebuilder:public BikeBuilder
{
public:
void buildTyre(){
bike->setTyre("摩拜轮胎");
}
void buildSeat(){
bike->setSeat("摩拜座位");
}
};
//ofo单车 具体构建者
class Ofobuilder:public BikeBuilder
{
public:
void buildTyre(){
bike->setTyre("ofo轮胎");
}
void buildSeat(){
bike->setSeat("ofo座位");
}
};
//指挥者
class Director{
private:
BikeBuilder* bikeBuilder;
public:
void setBikeBuilder(BikeBuilder *a){
bikeBuilder = a;
}
//组装
BikeProduct* construct(){
bikeBuilder->buildTyre();
bikeBuilder->buildSeat();
return bikeBuilder->bike;
}
};
int main(){
Director *it = new Director();
//建造摩拜单车
it->setBikeBuilder(new Mobiebuilder());
BikeProduct *mobie = it->construct();
cout<<mobie->getTyre()<<endl;
cout<<mobie->getSeat()<<endl;
//建造ofo单车
it->setBikeBuilder(new Ofobuilder());
mobie = it->construct();
cout<<mobie->getTyre()<<endl;
cout<<mobie->getSeat()<<endl;
}
【测试结果】
6、原型模式
若通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。原型模式以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点。克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。
优势
效率高(直接克隆,避免了重新执行构造步骤)。浅克隆与深克隆的区别
浅克隆:复制该对象,然后保留该对象原有的引用。也就是说不克隆该对象的属性。深克隆:复制该对象,并且把该对象的所有属性也克隆出一份新的。
深克隆的实现
创建了一个类模板
template<class T>
class ICloneable
{
public:
virtual T* clone() = 0;
};
接着创建一个简历类,继承ICloneable,并实现其纯虚函数
// 简历类
class CResume : public ICloneable<CResume>
{
protected:
string m_name;
string m_sex;
int m_age;
public:
CResume(){}
void setInfo(const string& name, const string& sex, int age)
{
m_name = name;
m_sex = sex;
m_age = age;
}
CResume* clone()
{
CResume* resume = new CResume;
resume->setInfo(m_name, m_sex, m_age);
return resume;
}
void printInfo()
{
cout << "Name: " << m_name << endl;
cout << "Sex: " << m_sex << endl;
cout << "Age: " << m_age << endl;
cout << endl;
}
};
【完整测试代码】
#include <iostream>
#include <string>
#include <memory>
using namespace std;
template<class T>
class ICloneable
{
public:
virtual T* clone() = 0;
};
// 简历类
class CResume : public ICloneable<CResume>
{
protected:
string m_name;
string m_sex;
int m_age;
public:
CResume(){}
void setInfo(const string& name, const string& sex, int age)
{
m_name = name;
m_sex = sex;
m_age = age;
}
CResume* clone()
{
CResume* resume = new CResume;
resume->setInfo(m_name, m_sex, m_age);
return resume;
}
void printInfo()
{
cout << "Name: " << m_name << endl;
cout << "Sex: " << m_sex << endl;
cout << "Age: " << m_age << endl;
cout << endl;
}
};
int main(void)
{
CResume re;
re.setInfo("Jacky", "Male", 20);
re.printInfo();
CResume* pClone = re.clone();
pClone->setInfo("Marry", "Female", 30);
pClone->printInfo();
re.printInfo();
delete pClone;
pClone = NULL;
return 0;
}
【测试结果】
可以看到,当pClone改变值后,re的信息没有被改变,就是深拷贝
浅拷贝实现
仅仅在深拷贝的clone函数改变了写法,没有调用构造函数了,改成直接将this指针赋值
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 克隆接口
template<class T>
class ICloneable
{
public:
virtual T* clone() = 0;
};
// 简历类
class CResume : public ICloneable<CResume>
{
protected:
string m_name;
string m_sex;
int m_age;
public:
CResume(){}
// 带参拷贝构造函数
CResume(CResume& right)
{
m_name = right.m_name;
m_sex = right.m_sex;
m_age = right.m_age;
}
void setInfo(const string& name, const string& sex, int age)
{
m_name = name;
m_sex = sex;
m_age = age;
}
CResume* clone()
{
// 指针赋值 浅拷贝
CResume* resume = this;
return resume;
}
void printInfo()
{
cout << "Name: " << m_name << endl;
cout << "Sex: " << m_sex << endl;
cout << "Age: " << m_age << endl;
cout << endl;
}
};
int main(void)
{
CResume *re = new CResume(); // 只能这样构造 无参构造被屏蔽掉了
re->setInfo("Jacky", "Male", 20);
re->printInfo();
CResume* pClone = re->clone(); // 指针赋值 浅拷贝
//delete pWorkExperience; // 这里如果调用delete 程序会崩溃(因为是共享)
pClone->setInfo("Marry", "Female", 30);
pClone->printInfo();
re->printInfo(); // 我们发现原对象的workExperience属性已经被修改
delete pClone;
pClone = NULL;
return 0;
}
【测试结果】
更改了pClone的信息,会发现,re的信息同样被改变了