设计模式 - 创建型模式 上(C++版)
- 一、设计模式的七大原则
- [二、单例模式(Singleton Pattern)](#二、单例模式(Singleton Pattern))
- [三、工厂模式(Factory Pattern)](#三、工厂模式(Factory Pattern))
- [四、抽象工厂模式(Abstract Factory Pattern)](#四、抽象工厂模式(Abstract Factory Pattern))
-
- 1、抽象工厂模式代码实现
- 2、优缺点
-
- [2.1 优点](#2.1 优点)
- [2.2 缺点](#2.2 缺点)
- 3、使用场景
- [五、创建者模式(Builder Pattern)](#五、创建者模式(Builder Pattern))
-
- 1、创建者模式代码实现
- 2、优缺点
-
- [2.1 优点](#2.1 优点)
- [2.2 缺点](#2.2 缺点)
- [2.3 创建者模式与抽象工厂模式的比较](#2.3 创建者模式与抽象工厂模式的比较)
- 3、使用场景
- [六、原型模式(Prototype Pattern)](#六、原型模式(Prototype Pattern))
一、设计模式的七大原则
1、开放封闭原则
- OCP,Open For Extension Closed For Modification Principle,简称开闭原则。开闭原则是指软件实体是可以扩展的,但是不可修改。也就是说,模块和函数是对扩展(提供方)开放的,对修改(使用方)关闭的,对于一个新的需求,对软件的改动应该是通过增加代码来实现的,而不是通过改动代码实现的。开闭原则是面向对象的核心,是最基础、最重要的设计原则,开发过程中,应该把可能会频繁变动的部分抽象出来,当需要变动时,只需要去实现抽象即可,也就是面向抽象编程。对于C++类来说,对类的改动是通过增加代码实现的,而不是修改代码实现的,通过虚基类的继承和虚函数的实现来完成一个类功能的扩充,这也是多态在设计模式中重要地位的体现。
2、单一职责原则
- SRP,Single Responsibility Principle,单一职责原则。对类来说,类的职责应该是单一的,一个类只能对外提供一种功能。换句话说,变动这个类的理由或动机只能有一个,如果第二个改动类的理由,就不是单一职责。单一职责相当于降低了各种职责的耦合度,如果一个类负责多个职责,那么改动类的某一职责时,可能会影响到类行使其他职责的能力。
3、依赖倒置原则
- DIP,Dependence Inversion Principle,依赖倒置原则。抽象不应该依赖于细节,细节应该依赖于抽象。换句话说,依赖于抽象接口,而不是依赖于具体的类的实现,也就是面向抽象接口编程。依赖倒置原则是面向对象编程的标志,在具体软件设计时,上层模块不应该依赖于底层模块,底层模块更不应该依赖上层模块,而是上层模块和底层模块都向中间靠拢,共同依赖于二者中间的抽象接口层。整个软件程序设计的依赖关系应该终止于抽象接口层,上层和底层互不关心,甚至使用什么编程语言都不关心。抽象接口层提供一个标准或者协议,它对上提供访问的接口,对下提供实现的标准,抽象接口层本身不执行任何操作,具体的功能由它的实现去完成。
4、接口隔离原则
- ISP,Interface Segegation Principle,接口隔离原则。一个接口对外只提供一种功能,不同功能应该通过不同的接口提供,而不是把多种功能都封装到一个接口中,否则的话,可能有的客户只需要功能A不需要功能B,但是提供A功能的接口内还封装了功能B,这就造成了客户被迫依赖某种他们不需要的功能。
5、里氏替换原则
- LSP,Liskov Substitution Principle,里氏替换原则。任何地方出现的抽象类,都可以使用该抽象类的实现类来代替,这其实类似于C++中的类型兼容性原则,所有基类出现的地方都可以用子类对象来代替。实际上,继承增强了类与类之间的耦合性,在继承中应该尽量不要重写(覆盖)基类中的非抽象方法,子类可以有自己的方法,但是不能重新定义或修改基类的方法,否则如果基类中发生改变,所有的子类都可能受影响,应该尽量使用组合或者聚合而不是继承。其实,准确来说,应该是尽量不要继承可实例化的类(非抽象类),而应该是从抽象类中继承。
6、合成复用原则
- CARP,Composite/Aggregate Reuse Principle,优先使用对象组合而不是继承原则。使用继承的时候,基类的变化可能会影响到子类,而如果使用组合/聚合就可以降低这种依赖关系。组合/聚合降低了类与类之间的耦合性,一个类的变化对另一个类的影响相对较小,如果要使用继承,必须要遵守里氏替换原则。
7、迪米特法则
- LOD,Law of Demeter,迪米特法则,也叫做最少知道原则(The Least Knowledge Principle)。一个类对于其它类知道的越少越好,只和朋友说话,不要和陌生人说话,这里的朋友是指作为成员变量、方法输入输出参数的类(朋友类),如果是出现在一个方法内部的类就不是朋友类。迪米特法则降低了耦合度,各个模块之间通过一个接口来实现调用,而模块之间不需要知道对方的内部实现逻辑,并且一个模块的内部改变也不会影响到另一个模块(黑盒原理)。如果两个类不直接通信,那么这两个类就不应当发生直接的相互作用。如果一个类需要调用另一个类的某个方法的话,可以通过第三个类转发这个调用。
8、关于设计模式
- 模式就是解决问题的固定套路,设计模式(Design pattern)就是一套经过前人反复使用,总结出来的程序设计经验。设计模式总共分为三大类:
- 第一类是创建型模式 ,该模式通常和对象的创建有关,涉及到对象实例化的方式。包括:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式五种;
- 第二类是结构型模式,结构型模式描述的是如何组合类和对象来获得更大的结构。包括:代理模式、装饰者模式、适配器模式、桥接模式、组合模式、外观模式、享元模式共7种模式。
- 第三种是行为型模式,用来描述对类或对象怎样交互和怎样分配职责。共有:模板模式、命令模式、责任链模式、策略模式、中介者模式、观察者模式、备忘录模式、访问者模式、状态模式、解释器模式、迭代器模式11种模式。
二、单例模式(Singleton Pattern)
- 介绍:单例模式属于创建型模式的一种,通常一个类可以实例化很多对象,而单例模式就是一个类在程序运行期间只创建一个对象。相当于一个全局对象,只对外提供一个接口去获取这个唯一对象。单例模式有两种:
懒汉式单例模式 :在第一次使用时再创建对象,而后面使用时都获取的是这个对象。
饿汉式单例模式:在类初始化时就创建好唯一的对象。
1、懒汉式单例及饿汉式单例代码实现
cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
std::mutex g_mutex;
/*
懒汉式单例模式:
*/
class LazySingleton{
public:
static LazySingleton* get_object(){
if(m_pLazySingleton == nullptr){
g_mutex.lock(); // 多线程中使用懒汉式单例模式加锁会让使得其线程安全
if(m_pLazySingleton == nullptr){
m_pLazySingleton = new LazySingleton;
}
g_mutex.unlock();
}
return m_pLazySingleton;
}
void print_count(){ // 用于测试是否懒汉式单例模式,如果是m_count会随着创建对象的数量递增,反之只会打印 1
g_mutex.lock();
m_count++;
std::cout<<"lazy m_count = "<<m_count<<std::endl;
g_mutex.unlock();
}
private:
LazySingleton(){
m_count = 0;
}
int m_count;
static LazySingleton* m_pLazySingleton;
};
LazySingleton* LazySingleton::m_pLazySingleton = nullptr; // 初始化时指向nullptr,使用时再创建
/*
饿汉式单例模式:
*/
class HungrySingleton{
public:
static HungrySingleton* get_object(){
return m_pHungrySingleton;
}
void print_count(){
g_mutex.lock();
m_count++;
std::cout<<"hungry m_count = "<<m_count<<std::endl;
g_mutex.unlock();
}
private:
HungrySingleton(){
m_count = 0;
}
int m_count;
static HungrySingleton* m_pHungrySingleton;
};
HungrySingleton* HungrySingleton::m_pHungrySingleton = new HungrySingleton; // 一开始就创建了对象
int main(void){
// 懒汉式单例模式
LazySingleton* pLazy_1 = LazySingleton::get_object();
pLazy_1->print_count();
LazySingleton* pLazy_2 = LazySingleton::get_object();
pLazy_2->print_count();
if(pLazy_2 == pLazy_1){
std::cout<<"yes"<<std::endl;
}
// 多线程测试
std::vector<std::thread> vec_thread;
for(size_t index = 0; index < 5; ++index){
vec_thread.push_back(std::thread(
[]{
LazySingleton* pLazy = LazySingleton::get_object();
pLazy->print_count();
}));
}
for(size_t index_ = 0; index_ < vec_thread.size(); ++index_){
vec_thread[index_].join();
}
/*----------------------------------------------------------------------------------------*/
// 饿汉式单例模式
vec_thread.clear();
HungrySingleton* pHungry_1 = HungrySingleton::get_object();
pHungry_1->print_count();
HungrySingleton* pHungry_2 = HungrySingleton::get_object();
pHungry_2->print_count();
if(pHungry_2 == pHungry_1){
std::cout<<"yes"<<std::endl;
}
// 多线程测试
for(size_t index = 0; index < 5; ++index){
vec_thread.push_back(std::thread(
[]{
HungrySingleton* pHungry = HungrySingleton::get_object();
pHungry->print_count();
}));
}
for(size_t index_ = 0; index_ < vec_thread.size(); ++index_){
vec_thread[index_].join();
}
return 0;
}
1.1、唯一的实例对象
- 一般情况下使用new 创建对象时,会调用类的构造函数实例化出一个对象。但是在单例模式中我们只需要一个对象,所以就不能随时随地的使用new 去创建对象了。那么就应该禁止类的外部访问构造函数,因为每次在类的外部调用构造函数都会构造出一个新的实例对象。解决办法就是把构造函数设置为私有属性,在类的内部完成实例化对象的创建,这样就对外隐藏了创建对象的方法。但是类的出现就是要定义对象的,我们要使用类创建的对象,所以还需要提供一个全局访问点来获取类内部创建好的对象。如上代码中就只提供了一个get_object 的接口获取类内部创建好的对象。如果第一次使用时会创建对象,后面获取到的都是第一次创建好的对象,这样保证了唯一的实例对象。为了让这个类所定义的所有对象共享属性,应该把属性设置为static类型,因为static类型的属性属于整个类而不是属于某个对象。
1.2、懒汉式单例与饿汉式单例的区别
- 单例模式主要有懒汉式和饿汉式两种实现,饿汉式不会有线程安全的问题,但是提前构造对象占用了一定的资源,如果对内存要求较低的场景可以使用饿汉式实现;懒汉式应使用锁来避免多线程竞争资源的问题,并且懒汉式可以在需要使用对象的时候才去创建对象,节省了资源。
三、工厂模式(Factory Pattern)
- 介绍:工厂模式也属于创建型模式,也称为多态模式。比如一个图书管理系统中提供了借书、还书等操作,在进行操作前要进行学生信息验证。那么就可以提供一个抽象操作类,然后使借书、还书等这些操作继承这个类并实现具体的操作。对于验证提供一个抽象验证类,然后不同操作时的验都继承抽象验证类,验证成功后返回具体的操作类对象。操作相当于产品,验证相当于工厂,每个工厂只生产一种产品。因此,工厂模式中有四种结构:
- (1)、抽象工厂:所有具体工厂都要实现这个接口
- (2)、具体工厂:负责实例化具体产品对象
- (3)、抽象产品:它是工厂类所创建的所有实例的类的共同基类,用于描述产品的公共接口
- (4)、具体产品:具体工厂类实例化出的具体对象
1、工厂模式代码实现
cpp
#include <iostream>
#include <string>
/*
工厂模式
*/
// 抽象操作类(抽象产品类)
class Operation{
public:
virtual void operation() = 0;
};
// 具体操作类-借书操作(具体产品类)
class BorrowBook : public Operation{
public:
void operation(){
std::cout<<"借书成功"<<std::endl;
}
};
// 具体操作类-还书操作(具体产品类)
class ReturnBook : public Operation{
public:
void operation(){
std::cout<<"还书成功"<<std::endl;
}
};
// 学生信息
struct StudentMessage{
std::string name;
int id_code;
};
// 抽象验证类(抽象工厂类)
class Verification{
public:
virtual Operation* verification(StudentMessage& msg) = 0;
};
// 具体验证类-验证学生信息并返回借书操作(具体工厂类)
class BorrowBookVerification : public Verification{
Operation* verification(StudentMessage& msg){
if(msg.id_code == 1111 && msg.name == "ZhangSan"){
return new BorrowBook;
}else{
return nullptr;
}
}
};
// 具体验证类-验证学生信息并返回还书操作(具体工厂类)
class ReturnBookVerification : public Verification{
Operation* verification(StudentMessage& msg){
if(msg.id_code == 1111 && msg.name == "ZhangSan"){
return new ReturnBook;
}else{
return nullptr;
}
}
};
int main(void){
Operation* iper = nullptr;
Verification* veri = nullptr;
StudentMessage msg{
name : "ZhangSan",
id_code : 1111
};
// 借书流程
veri = new BorrowBookVerification;
iper = veri->verification(msg); // 通过借书工厂类生产出具体的借书操作
if(iper == nullptr){
std::cout<<"验证失败"<<std::endl;
return 0;
}
iper->operation(); // 使用具体的产品(借书)
delete iper;
delete veri;
//还书流程
veri = new ReturnBookVerification;
msg.id_code = 1110; //这里修改了id_code使其验证失败不能进行具体操作
iper = veri->verification(msg); // 通过还书工厂类生产出具体的还书操作,如果信息不对就表明此工厂不生产该产品
if(iper == nullptr){
std::cout<<"验证失败"<<std::endl;
delete veri;
veri = nullptr;
return 0;
}
iper->operation(); // 使用具体的产品(还书)
delete iper;
iper = nullptr;
delete veri;
veri = nullptr;
return 0;
}
1.1、为什么要用工厂模式
- 工厂模式提供了一个抽象的工厂接口,由具体工厂去创建产品实例,极大的方便了增加产品,删除产品等操作,且很好的符合了开闭原则,需要增加操作的话工厂模式只需要增加一个具体工厂类和一个具体产品类就可以完成功能的扩展。
四、抽象工厂模式(Abstract Factory Pattern)
- 介绍:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。属于对象创建型模式。比如一个app有两个界面每个界面都有两个功能,怎么用抽象工厂模式实现这个架构呢?可以定义一个抽象功能类(抽象产品),然后每个界面的每个功能都继承这个抽象功能类,这样就会有四个具体功能类(具体产品)。再定义一个抽象界面类(抽象工厂),每个界面具体界面继承抽象界面类,就会有两个具体界面类(具体工厂),每个界面管理自己的两个功能。
- 抽象工厂模式的四种结构:
(1)、抽象工厂角色:所有具体工厂都要实现这个接口,可以创建多个不同等级的产品(多条产品线);
(2)、具体工厂:负责实例化具体产品对象,多条产品线;
(3)、抽象角色:和简单工厂模式一样,它是工厂类所创建的所有实例的类的共同基类,用于描述产品的公共接口。
(3)、具体产品角色:具体工厂类所要实例化的对象。
1、抽象工厂模式代码实现
cpp
#include <iostream>
/*
抽象工厂模式
*/
// 抽象功能类 (抽象产品类)
class Function{
public:
virtual void function() = 0;
};
// 接收消息功能 - 具体功能类 (具体产品类)
class ReceiveMessage : public Function{
public:
void function(){
std::cout<<"接收消息\n";
}
};
// 查看消息功能 - 具体功能类 (具体产品类)
class ViewMessage : public Function{
public:
void function(){
std::cout<<"查看消息\n";
}
};
// 查看联系人功能 - 具体功能类 (具体产品类)
class ViewContactsInfor : public Function{
public:
void function(){
std::cout<<"查看联系人信息\n";
}
};
// 查看公众号功能 - 具体功能类 (具体产品类)
class ViewOfficialAccounts : public Function{
public:
void function(){
std::cout<<"查看公众号\n";
}
};
// 抽象界面类 (抽象工厂类)
class WeChatInterface{
public:
virtual Function* funcation_1() = 0; // 功能接口
virtual Function* funcation_2() = 0;
};
// 消息界面(包含接收消息和查看消息功能) - 具体界面类 (具体工厂类)
class WeChat : public WeChatInterface{
public:
Function* funcation_1(){
return new ReceiveMessage; // 接受消息功能
}
Function* funcation_2(){
return new ViewMessage; // 查看消息功能
}
};
// 联系人界面(包含查看联系人信息和查看公众号功能) - 具体界面类 (具体工厂类)
class Contacts : public WeChatInterface{
public:
Function* funcation_1(){
return new ViewContactsInfor; // 查看联系人信息功能
}
Function* funcation_2(){
return new ViewOfficialAccounts; // 查看公众号功能
}
};
int main(void){
Function* func = nullptr;
WeChatInterface* wechatInter = nullptr;
// 创建消息界面
wechatInter = new WeChat;
// 使用功能 1
func = wechatInter->funcation_1();
func->function();
delete func;
func = nullptr;
// 使用功能 2
func = wechatInter->funcation_2();
func->function();
delete func;
func = nullptr;
delete wechatInter;
wechatInter = nullptr;
// 创建联系人界面
wechatInter = new Contacts;
// 使用功能 1
func = wechatInter->funcation_1();
func->function();
delete func;
func = nullptr;
// 使用功能 2
func = wechatInter->funcation_2();
func->function();
delete func;
func = nullptr;
delete wechatInter;
wechatInter = nullptr;
return 0;
}
2、优缺点
2.1 优点
(1)、抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
(2)、当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
(3)、增加新的具体工厂和产品族很方便,无须修改已有系统。(比如上面的示例要增加一个界面并且界面只有两个功能时比较方便)
2.2 缺点
(1)、在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。(比如只在其中一个已有的界面增加一个功能,而另一个界面不需要这个功能时)
(2)、开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)
3、使用场景
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
五、创建者模式(Builder Pattern)
- 介绍:创建者模式,也叫做生成器模式,是一种对象创建型模式。创建者模式用于创建具有多个部件的复合对象,并隐藏了复合对象的创建过程,不同的部件建造者有不同的建造方法。通过建造者模式实现了对象的构建和对象的表示的分离,也就是说,通过同样的构建过程可以创建出不同的表示对象。
- 创建者模式中的结构:
(1)、抽象创建者:为建造各个组件提供统一的抽象接口;
(2)、具体创建者:实现抽象建造者提供的抽象接口,定义各个组件的建造方法,是组件建造的具体实施者;
(3)、指挥者:调用具体建造者来建造产品的各个组件,指挥者并不知道产品的具体信息,指挥者只负责规定并保证各个组件的建造过程和建造逻辑;
(4)、产品:被建造的复杂对象,包含组合对象的各个部件;
1、创建者模式代码实现
cpp
#include <iostream>
#include <string>
/*
建造者模式
*/
// 最终NPC类 - (最终产品类)
class NPC{
public:
void set_kind(std::string kind){ m_kind = kind; }
void set_gender(std::string gender){ m_gender = gender;}
void set_bodily(std::string bodily){ m_bodily = bodily; }
void set_attribute(std::string attribute){ m_attribute = attribute; }
void set_weapon(std::string weapon){ m_weapon = weapon; }
std::string get_kind(){return m_kind;}
std::string get_gender(){return m_gender;}
std::string get_bodily(){return m_bodily;}
std::string get_attribute(){return m_attribute;}
std::string get_weapon(){return m_weapon;}
private:
std::string m_kind; // 种类
std::string m_gender; // 性别
std::string m_bodily; // 体型
std::string m_attribute; // 属性
std::string m_weapon; // 武器
};
// 抽象创建类 - (抽象创建者)
class CreateNPC{
public:
virtual void set_kind(std::string kind) = 0;
virtual void set_gender(std::string gender) = 0;
virtual void set_bodily(std::string bodily) = 0;
virtual void set_attribute(std::string attribute) = 0;
virtual void set_weapon(std::string weapon) = 0;
virtual NPC* get_npc() = 0;
};
// 具体创建NPC类 - (具体创建类)
class PeopleNPC : public CreateNPC{
public:
PeopleNPC(){
m_npc = new NPC;
m_npc->set_kind("人类"); // 这里是PeopleNPC的创建者,所有默认类型是*人类*
}
void set_kind(std::string kind){
m_npc->set_kind(kind);
}
void set_gender(std::string gender){
m_npc->set_gender(gender);
}
void set_bodily(std::string bodily){
m_npc->set_bodily(bodily);
}
void set_attribute(std::string attribute){
m_npc->set_attribute(attribute);
}
void set_weapon(std::string weapon){
m_npc->set_weapon(weapon);
}
NPC* get_npc(){
return m_npc;
}
private:
NPC* m_npc;
};
// 玩家类 - (指挥者类)
class Player{
public:
Player(CreateNPC* createrNPC){
m_createrNPC = createrNPC;
}
CreateNPC* createrNPC(){
return m_createrNPC;
}
private:
CreateNPC* m_createrNPC;
};
int main(void){
NPC* npc = nullptr;
CreateNPC* create = nullptr;
Player* player = nullptr;
// 通过那个创建这创建(指挥者指挥谁去创建),这里指挥PeopleNPC创建一个人类NPC
create = new PeopleNPC;
player = new Player(create);
// 指挥者告知创建这如何创建
player->createrNPC()->set_attribute("金属性");
player->createrNPC()->set_bodily("胖");
player->createrNPC()->set_gender("男");
player->createrNPC()->set_weapon("大刀");
// 创建者创建完成后交付产品
npc = player->createrNPC()->get_npc();
// 产品展示
std::cout<<"==================\n";
std::cout<<"NPC 信息:\n";
std::cout<<"类型:"<<npc->get_kind()<<"\n";
std::cout<<"属性:"<<npc->get_attribute()<<"\n";
std::cout<<"体型:"<<npc->get_bodily()<<"\n";
std::cout<<"性别:"<<npc->get_gender()<<"\n";
std::cout<<"武器:"<<npc->get_weapon()<<"\n";
return 0;
}
2、优缺点
2.1 优点
(1)在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
(2)每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
(3)可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
(4)增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合"开闭原则"
2.2 缺点
(1)创建者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
(2)如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
2.3 创建者模式与抽象工厂模式的比较
- 与抽象工厂模式相比, 创建者模式返回一个组装好的完整产品 ,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
- 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
- 如果将抽象工厂模式看成汽车配件生产工厂 ,生产一个产品族的产品,那么建造者模式就是一个 汽车组装工厂 ,通过对部件的组装可以返回一辆完整的汽车。
3、使用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在创建者类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
六、原型模式(Prototype Pattern)
- 介绍:原型模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例,被复制出来的对象具有和原型一摸一样的数据,并且在通过复制创造另一个一模一样的对象时,不需要知道创造的过程。根据对象克隆深度层次的不同,有浅度克隆与深度克隆。
1、原型模式代码实现
cpp
#include <iostream>
#include <string>
/*
原型模式
*/
class Clone{
public:
virtual Clone* clone() = 0;
virtual void print_message() = 0;
};
class Man : public Clone{
public:
Man(int age, float height, std::string name) : m_age(age),m_height(height), m_name(name) {};
Man(){};
Clone* clone(){
// 深拷贝
Man* temp = new Man;
*temp = *this;
return temp;
// 浅拷贝
// return this;
}
void print_message(){
std::cout<<"age: "<<m_age<<std::endl;
std::cout<<"height: "<<m_height<<std::endl;
std::cout<<"name: "<<m_name<<std::endl;
}
private:
int m_age;
float m_height;
std::string m_name;
};
int main(void){
Clone* p_map = new Man(12, 156.7, "Ann");
p_map->print_message();
Clone* p_map_ = p_map->clone();
p_map_->print_message();
delete p_map;
p_map = nullptr;
delete p_map_; //浅拷贝不用delete,因为上面已经delete了
p_map_ = nullptr;
return 0;
}
2、使用场景
- 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。
- 原型模式为客户端代码提供一个通用接口, 客户端代码可通过这一接口与所有实现了克隆的对象进行交互, 它也使得客户端代码与其所克隆的对象具体类独立开来。
- 在原型模式中, 你可以使用一系列预生成的、 各种类型的对象作为原型。
- 客户端不必根据需求对子类进行实例化, 只需找到合适的原型并对其进行克隆即可。