设计模式概述
设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的解决某一类问题的一种编码方案。
设计模式特点
类型 | 设计模式 | 描述 |
---|---|---|
创建型 | Factory 模式 | 被实例化的子类 |
AbstactFactory 模式 | 产品对象家族 | |
Singleton 模式 | 针对一个类的唯一实例 | |
Builder 模式 | 如何创建一个组合对象 | |
Prototype 模式 | 针对被实例化的类 | |
结构型 | Bridge 模式 | 对象的实现 |
Adapter 模式 | 针对对象的接口 | |
Decorator 模式 | 对对象的职责,不生成子类 | |
Composite 模式 | 一个对象的结构和组成 | |
Flyweight 模式 | 对象的存储开销 | |
Facade 模式 | 对一个子系统的接口 | |
Proxy 模式 | 如何访问一个对象;该对象的位置 | |
行为型 | Template 模式 | 对算法中的某些步骤 |
Strategy 模式 | 算法 | |
State 模式 | 对象的状态 | |
Observer 模式 | 对多个对象依赖于另外一个对象,而这些对象又如何保持一致 | |
Memento 模式 | 对一个对象中哪些私有信息存放在该对象之外,以及在对什么时候进行存储 | |
Mediator 模式 | 对象间怎样交互、和谁交互 | |
Command 模式 | 何时、怎样满足一个请求 | |
Visitor 模式 | 某些可作用于一个(组)对象上的操作,但不修改这些对象的类 | |
Chain of Responsibility 模式 | 满足一个请求的对象链 | |
Iterator 模式 | 如何遍历、访问一个聚合的各元素 | |
Interpreter 模式 | 对一个语言的文法及解释 |
一、创建型模式
Factory 模式(工厂模式)
在面向对象系统设计中经常可以遇到以下的两类问题:
- 我们经常会抽象出一些类的公共接口以形成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子类实现,达到了多态的目的。所以就不得不在要用到子类的地方写new 对象。这样实体类的使用者必须知道实际的子类名称,以及会使程序的扩展性和维护变得越来越困难。
- 还有一种情况就是在父类中并不知道具体要实例化哪一个具体的子类。只能在父类中写方法调用,具体调用哪一个类的方法交给子类实现。
以上两个问题也就引出了 Factory 模式的两个最重要的功能:
1)定义创建对象的接口,封装了对象的创建。
2)使得具体化类的工作延迟到了子类中。
举例:
代码:
//Product.h
#ifndef _PRODUCT_H_
#define _PRODUCT_H_
class Product
{
public:
virtual ~Product() = 0;
protected:
Product();
private:
};
class ConcreteProduct:public Product
{
public:
~ConcreteProduct();
ConcreteProduct();
protected:
private:
};
#endif //~_PRODUCT_H_
//Product.cpp
#include "Product.h"
#include <iostream>
using namespace std;
Product::Product()
{
}
Product::~Product()
{
}
ConcreteProduct::ConcreteProduct()
{
cout<<"ConcreteProduct...."<<endl;
}
ConcreteProduct::~ConcreteProduct()
{
}
//Factory.h
#ifndef _FACTORY_H_
#define _FACTORY_H_
class Product;
class Factory
{
public:
virtual ~Factory() = 0;
virtual Product* CreateProduct() = 0;
protected:
Factory();
private:
class ConcreteFactory:public Factory
{
public:
~ConcreteFactory();
ConcreteFactory();
Product* CreateProduct();
protected:
private:
};
#endif //~_FACTORY_H_
//Factory.cpp
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
Factory::Factory()
{
}
Factory::~Factory()
{
}
ConcreteFactory::ConcreteFactory()
{
cout<<"ConcreteFactory....."<<endl;
}
ConcreteFactory::~ConcreteFactory()
{
}
Product* ConcreteFactory::CreateProduct()
{
return new ConcreteProduct();
}
//main.cpp
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
Factory* fac = new ConcreteFactory();
Product* p = fac->CreateProduct();
return 0;
}
总结:
- 如果为每一个具体的 ConcreteProduct 类的实例化提供一个函数体,那么我们可能不得不在系统中添加了一个方法来处理这个新建的
ConcreteProduct - 可以看出,Factory 模式对于对象的创建给予开发人员提供了很好的实现策略,但是Factory 模式仅仅局限于一类类(就是说
Product 是一类,有一个共同的基类),如果我们要为不同类的类提供一个对象创建的接口,那就要用 AbstractFactory 了。
AbstactFactory 模式(抽象工厂模式)
假如我们要买水果,水果的产地来自中国、日本、美国,每个国家的水果种类都可以分为苹果、香蕉、梨子。作为开发者,我们就不得不创建苹果类(香蕉和梨子类似),然后每种苹果都继承自苹果类。每上架一个国家的苹果我们都要实现一次苹果类,这样就会有成千上万的苹果类需要被创建,AbstractFactory 模式就是用来解决这类问题的:要创建一组相关或者相互依赖的对象。
举例:
代码:
//抽象工厂模式
#include <iostream>
using namespace std;
//苹果的抽象
class AbstractApple {
public:
virtual void showName() = 0;
};
//中国苹果
class ChinaApple :public AbstractApple {
public:
virtual void showName() {
cout << "中国苹果" << endl;
}
};
//美国苹果
class USAApple :public AbstractApple {
public:
virtual void showName() {
cout << "美国苹果" << endl;
}
};
//日本苹果
class JapanApple :public AbstractApple {
public:
virtual void showName() {
cout << "日本苹果" << endl;
}
};
//香蕉的抽象
class AbstractBanana {
public:
virtual void showName() = 0;
};
//中国香蕉
class ChinaBanana :public AbstractBanana {
public:
virtual void showName() {
cout << "中国香蕉" << endl;
}
};
//美国香蕉
class USABanana :public AbstractBanana {
public:
virtual void showName() {
cout << "美国香蕉" << endl;
}
};
//日本香蕉
class JapanBanana :public AbstractBanana {
public:
virtual void showName() {
cout << "日本香蕉" << endl;
}
};
//鸭梨的抽象
class AbstractPear {
public:
virtual void showName() = 0;
};
//中国鸭梨
class ChinaPear :public AbstractPear {
public:
virtual void showName() {
cout << "中国鸭梨" << endl;
}
};
//美国鸭梨
class USAPear :public AbstractPear {
public:
virtual void showName() {
cout << "美国鸭梨" << endl;
}
};
//日本鸭梨
class JapanPear :public AbstractPear {
public:
virtual void showName() {
cout << "日本鸭梨" << endl;
}
};
//抽象工厂 针对产品族
class AbstractFactory {
public:
virtual AbstractApple* CreateApple() = 0;
virtual AbstractBanana* CreateBanana() = 0;
virtual AbstractPear* CreatePear() = 0;
};
//中国工厂
class ChinaFactory :public AbstractFactory {
virtual AbstractApple* CreateApple() {
return new ChinaApple;
}
virtual AbstractBanana* CreateBanana() {
return new ChinaBanana;
}
virtual AbstractPear* CreatePear() {
return new ChinaPear;
}
};
//美国工厂
class USAFactory :public AbstractFactory {
virtual AbstractApple* CreateApple() {
return new USAApple;
}
virtual AbstractBanana* CreateBanana() {
return new USABanana;
}
virtual AbstractPear* CreatePear() {
return new USAPear;
}
};
//日本工厂
class JapanFactory :public AbstractFactory {
virtual AbstractApple* CreateApple() {
return new JapanApple;
}
virtual AbstractBanana* CreateBanana() {
return new JapanBanana;
}
virtual AbstractPear* CreatePear() {
return new JapanPear;
}
};
void test01() {
AbstractFactory* factory = NULL;
AbstractApple* apple = NULL;
AbstractBanana* Banana = NULL;
AbstractPear* Pear = NULL;
//中国工厂
factory = new ChinaFactory;
apple = factory->CreateApple();
Banana = factory->CreateBanana();
Pear = factory->CreatePear();
apple->showName();
Banana->showName();
Pear->showName();
delete Pear;
delete apple;
delete Banana;
delete factory;
}
int main()
{
test01();
}
总结:
- AbstractFactory 模式和 Factory模式的区别是初学(使用)设计模式时候的一个容易引起困惑的地方。实际上,AbstractFactory模式是为创建一组(有多类)相关或依赖的对象提供创建接口,而 Factory模式是为一类对象提供创建接口或延迟对象的创建到子类中实现。并且可以看到,AbstractFactory模式通常都是使用 Factory 模式实现。
Singleton 模式( 单例模式)
Singleton 模式是设计模式中最为简单、最为常见、最容易实现,也是最应该熟悉和掌握的模式。Singleton 模式就是一个类只创建一个唯一的对象,即一次创建多次使用。
实现单例模式的步骤:
1、构造函数私有化
2、增加静态私有的当前类的指针变量
3、提供静态对外接口,可以让用户获得单例对象
单例分为懒汉式和饿汉式
懒汉式:解决了饿汉式内存浪费问题,但是线程不安全的,可以通过互斥量mutex.lock()和mutex.unlock()来解决
饿汉式:还没有使用该单例对象,该单例对象就已经被加载到内存了,在对象过多时会造成内存浪费
代码:
#include <iostream>
using namespace std;
class A {
public:
static A* getInstace() {
return a;
}
private :
A() {
a = new A;
}
static A* a;
};
A* A::a = NULL;
//懒汉式 对象的创建在第一次调用getInstance函数时创建
//懒汉式是线程不安全的
class SingletonLazy {
public:
static SingletonLazy* getInstance() {
if (pSingleton == NULL) {
pSingleton = new SingletonLazy;
}
return pSingleton;
}
private:
SingletonLazy() {}
static SingletonLazy* pSingleton;
};
//在类外面进行初始化
SingletonLazy* SingletonLazy::pSingleton=NULL;
//饿汉式 对象在程序执行时优先创建
//饿汉式是线程安全的
class SingletonHungry {
public:
static SingletonHungry* getInstance() {
return pSingleton;
}
static void freeSpace() {
if (pSingleton != NULL) {
delete pSingleton;
}
}
private:
SingletonHungry() {}
static SingletonHungry* pSingleton;
};
//以下语句将会在main函数运行前执行
SingletonHungry* SingletonHungry::pSingleton=new SingletonHungry;
void test01() {
SingletonLazy* p1 = SingletonLazy::getInstance();
SingletonLazy* p2 = SingletonLazy::getInstance();
if (p1 == p2) {
cout << "单例模式" << endl;
}
else {
cout << "不是单例模式" << endl;
}
SingletonHungry* p3 = SingletonHungry::getInstance();
SingletonHungry* p4 = SingletonHungry::getInstance();
if (p3 == p4) {
cout << "单例模式" << endl;
}
else {
cout << "不是单例模式" << endl;
}
}
int main()
{
test01();
}
总结:
- Singleton 不可以被实例化,因此将其构造函数声明private
- Singleton 模式经常和 Factory(AbstractFactory)模式在一起使用,因为系统中工厂对象一般来说只要一个
Builder 模式(建造者模式)
生活中有着很多的Builder的例子,个人觉得大学生活就是一个Builder模式的最好体验:要完成大学教育,一般将大学教育过程分成 4 个学期进行,因此没有学习可以看作是构建完整大学教育的一个部分构建过程,每个人经过这 4 年的(4 个阶段)构建过程得到的最后的结果不一样,因为可能在四个阶段的构建中引入了很多的参数(每个人的机会和际遇不完全相同)
Builder 模式要解决的也正是这样的问题:当我们要创建的对象很复杂的时候(通常是由很多其他的对象组合而成),我们要要复杂对象的创建过程和这个对象的表示(展示)分离开来,这样做的好处就是通过一步步的进行复杂对象的构建,由于在每一步的构造过程中可以引入参数,使得经过相同的步骤创建最后得到的对象的展示不一样。
举例:
代码:
//Product.h
#ifndef _PRODUCT_H_
#define _PRODUCT_H_
class Product
{
public:
Product();
~Product();
void ProducePart();
protected:
private:
};
class ProductPart
{
public:
ProductPart();
~ProductPart();
ProductPart* BuildPart();
protected:
private:
};
#endif //~_PRODUCT_H_
//Component.cpp
#include "Component.h"
Component::Component()
{
}
Component::~Component()
{
}
void Component::Add(const Component&
com)
{
}
Component* Component::GetChild(int index)
{
return 0;
}
void Component::Remove(const Component&
com)
{
}
//Builder.h
#ifndef _BUILDER_H_
#define _BUILDER_H_
#include <string>
using namespace std;
class Product;
class Builder
{
public:
virtual ~Builder();
virtual void BuildPartA(const string&
buildPara) = 0;
virtual void BuildPartB(const string&
buildPara) = 0;
virtual void BuildPartC(const string&
buildPara) = 0;
virtual Product* GetProduct() = 0;
protected:
Builder();
private:
};
class ConcreteBuilder:public Builder
{
public:
ConcreteBuilder();
~ConcreteBuilder();
void BuildPartA(const string&
buildPara);
void BuildPartB(const string& buildPara);
void BuildPartC(const string& buildPara);
Product* GetProduct();
protected:
private:
};
#endif //~_BUILDER_H_
//Builder.cpp
#include "Builder.h"
#include "Product.h"
#include <iostream>
using namespace std;
Builder::Builder()
{
}
Builder::~Builder()
{
}
ConcreteBuilder::ConcreteBuilder()
{
}
ConcreteBuilder::~ConcreteBuilder()
{
}
void ConcreteBuilder::BuildPartA(const string& buildPara)
{
cout<<"Step1:Build PartA..."<<buildPara<<endl;
}
void ConcreteBuilder::BuildPartB(const string& buildPara)
{
cout<<"Step1:Build PartB..."<<buildPara<<endl;
}
void ConcreteBuilder::BuildPartC(const string& buildPara)
{
cout<<"Step1:Build PartC..."<<buildPara<<endl;
}
Product* ConcreteBuilder::GetProduct()
{
BuildPartA("pre-defined");
BuildPartB("pre-defined");
BuildPartC("pre-defined");
return new Product();
}
//Director.h
#ifndef _DIRECTOR_H_
#define _DIRECTOR_H_
class Builder;
class Director
{
public:
Director(Builder* bld);
~Director();
void Construct();
protected:
private:
Builder* _bld;
};
#endif //~_DIRECTOR_H_
//Director.cpp
#include "director.h"
#include "Builder.h"
Director::Director(Builder* bld)
{
_bld = bld;
}
Director::~Director()
{
}
void Director::Construct()
{
_bld->BuildPartA("user-defined");
_bld->BuildPartB("user-defined");
_bld->BuildPartC("user-defined");
}
//main.cpp
#include "Builder.h"
#include "Product.h"
#include "Director.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
Director* d = new Director(new
ConcreteBuilder());
d->Construct();
return 0;
}
总结:
- Builder 模式的关键是其中的 Director对象并不直接返回对象,而是通过一步步(BuildPartA,BuildPartB,BuildPartC)来一步步进行对象的创建。
- Builder 模式的示例代码中,BuildPart 的参数是通过客户程序员传入的,这里使用“user-defined”代替,实际的可在 Construct 方法中传入这 3 个参数,这样就可以得到不同的细微差别的复杂对象了。
Builder 模式和 AbstractFactory 模式在功能上很相似,因为都是用来创建大的复杂的对象,它们的区别是Builder 模式强调的是一步步创建对象,并通过相同的创建过程可以获得不同的结果对象,一般来说 Builder 模式中对象不是直接返回的。而在 AbstractFactory 模式中对象是直接返回的,AbstractFactory 模式强调的是为创建多个相互依赖的对象提供一个同一的接口。
Prototype 模式(原型模式)
定义:Prototype 模式也正是提供了自我复制的功能,就是说新对象的创建可以通过已有对象进行创建。在 C++中拷贝构造函数(Copy Constructor)曾经是很对程序员的噩梦,拷贝又分为浅拷贝和深拷贝
浅拷贝:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深拷贝:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。clone明显是深复制,clone出来的对象是是不能去影响原型对象的
Prototype 模式提供了一个通过已存在对象进行新对象创建的接口(Clone),Clone接口在 C++中我们将通过拷贝构造函数实现。
举例:
代码:
//Prototype.h
#ifndef _PROTOTYPE_H_
#define _PROTOTYPE_H_
class Prototype
{
public:
virtual ~Prototype();
virtual Prototype* Clone() const = 0;
protected:
Prototype();
private:
};
class ConcretePrototype:public Prototype
{
public:
ConcretePrototype();
ConcretePrototype(const
ConcretePrototype& cp);
~ConcretePrototype();
Prototype* Clone() const;
protected:
private:
};
#endif //~_PROTOTYPE_H_
//Prototype.cpp
#include "Prototype.h"
#include <iostream>
using namespace std;
Prototype::Prototype()
{
}
Prototype::~Prototype()
{
}
Prototype* Prototype::Clone() const
{
return 0;
}
ConcretePrototype::ConcretePrototype()
{
}
ConcretePrototype::~ConcretePrototype()
{
}
ConcretePrototype::ConcretePrototype(const
ConcretePrototype& cp)
{
cout<<"ConcretePrototype copy ..."<<endl;
}
Prototype* ConcretePrototype::Clone() const
{
return new ConcretePrototype(*this);
}
//main.cpp
#include "Prototype.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
Prototype* p = new ConcretePrototype();
Prototype* p1 = p->Clone();
return 0;
}
总结:
- Prototype 模式通过复制原型(Prototype)而获得新对象创建的功能,这里 Prototype本身就是“对象工厂”(因为能够生产对象),实际上 Prototype 模式和 Builder 模式、AbstractFactory模式都是通过一个类(对象实例)来专门负责对象的创建工作(工厂对象),它们之间的区别是:Builder模式重在复杂对象的一步步创建(并不直接返回对象),AbstractFactory 模式重在产生多个相互依赖类的对象,而 Prototype模式重在从自身复制自己创建新类。
- 原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制
二、结构型模式
Bridge 模式(桥接模式)
假如遇到这样的问题:
1)客户给了你一个需求,于是使用一个类来实现(A);
2)客户需求变化,有两个算法实现功能,于是改变设计,我们通过一个抽象的基类,再定义两个具体类实现两个不同的算法(A1 和 A2);
3)客户又告诉我们说对于不同的操作系统,于是再抽象一个层次,作为一个抽象基类A0,在分别为每个操作系统派生具体类(A00 和 A01,其中 A00 表示原来的类 A)实现不同操作系统上的客户需求,这样我们就有了一共 4 个类。
4)可能用户的需求又有变化,比如说又有了一种新的算法………
5)我们陷入了一个需求变化的郁闷当中,也因此带来了类的迅速膨胀。Bridge 模式则正是解决了这类问题
示例:
代码:
//Abstraction.h
#ifndef _ABSTRACTION_H_
#define _ABSTRACTION_H_
class AbstractionImp;
class Abstraction
{
public:
virtual ~Abstraction();
virtual void Operation() = 0;
protected:
Abstraction();
private:
};
class RefinedAbstraction:public Abstraction
{
public:
RefinedAbstraction(AbstractionImp* imp);
~RefinedAbstraction();
void Operation();
protected:
private:
AbstractionImp* _imp;
};
#endif //~_ABSTRACTION_H_
//Abstraction.cpp
#include "Abstraction.h"
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
Abstraction::Abstraction()
{
}
Abstraction::~Abstraction()
{
}
RefinedAbstraction::RefinedAbstraction(AbstractionImp* imp)
{
_imp = imp;
}
RefinedAbstraction::~RefinedAbstraction()
{
}
void RefinedAbstraction::Operation()
{
_imp->Operation();
}
//AbstractionImp.h
#ifndef _ABSTRACTIONIMP_H_
#define _ABSTRACTIONIMP_H_
class AbstractionImp
{
public:
virtual ~AbstractionImp();
virtual void Operation() = 0;
protected:
AbstractionImp();
private:
};
class ConcreteAbstractionImpA:public AbstractionImp
{
public:
ConcreteAbstractionImpA();
~ConcreteAbstractionImpA();
virtual void Operation();
protected:
private:
};
class ConcreteAbstractionImpB:public AbstractionImp
{
public:
ConcreteAbstractionImpB();
~ConcreteAbstractionImpB();
virtual void Operation();
protected:
private:
};
#endif //~_ABSTRACTIONIMP_H_
//AbstractionImp.cpp
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
AbstractionImp::AbstractionImp()
{
}
AbstractionImp::~AbstractionImp()
{
}
void AbstractionImp::Operation()
{
cout<<"AbstractionImp....imp..."<<endl;
}
ConcreteAbstractionImpA::ConcreteAbstractionImpA()
{
}
ConcreteAbstractionImpA::~ConcreteAbstractionImpA()
{
}
void ConcreteAbstractionImpA::Operation()
{
cout<<"ConcreteAbstractionImpA...."<<endl;
}
ConcreteAbstractionImpB::ConcreteAbstractionImpB()
{
}
ConcreteAbstractionImpB::~ConcreteAbstractionImpB()
{
}
void ConcreteAbstractionImpB::Operation()
{
cout<<"ConcreteAbstractionImpB...."<<endl;
}
//main.cpp
#include "Abstraction.h"
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
AbstractionImp* imp = new ConcreteAbstractionImpA();
Abstraction* abs = new RefinedAbstraction(imp);
abs->Operation();
return 0;
}
总结:
- 桥接模式实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。
- 对于两个独立变化的维度,使用桥接模式再适合不过了。
- 分离抽象接口及其实现部分。提高了比继承更好的解决方案
Adapter 模式(适配器模式)
为了完成某项工作购买了一个第三方的库来加快开发。这就带来了一个问题:我们在应用程序中已经设计好了接口,与这个第三方提供的接口不一致,为了使得这些接口不兼容的类(不能在一起工作)可以在一起工作了,Adapter 模式提供了将一个类(第三方库)的接口转化希望的接口。
适配器模式分为类模式和对象模式。
1)采用继承原有接口类的方式
2)采用组合原有接口类的方式
在 Adapter 模式的结构图中可以看到,类模式的 Adapter 采用继承的方式复用 Adaptee的接口,而在对象模式的 Adapter 中我们则采用组合的方式实现 Adaptee 的复用。
代码:(只演示采用组合的方式)
//Adapter.h
#ifndef _ADAPTER_H_
#define _ADAPTER_H_
class Target
{
public:
Target();
virtual ~Target();
virtual void Request();
};
class Adaptee
{
public:
Adaptee();
~Adaptee();
void SpecificRequest();
};
class Adapter:public Target
{
public:
Adapter(Adaptee* ade);
~Adapter();
void Request();
private:
Adaptee* _ade;
};
#endif //~_ADAPTER_H_
//Adapter.cpp
#include "Adapter.h"
#include <iostream>
Target::Target()
{
}
Target::~Target()
{
}
void Target::Request()
{
std::cout<<"Target::Request"<<std::endl;
}
Adaptee::Adaptee()
{
}
Adaptee::~Adaptee()
{
}
void Adaptee::SpecificRequest()
{
std::cout<<"Adaptee::SpecificRequest"<<std::endl;
}
Adapter::Adapter(Adaptee* ade)
{
this->_ade = ade;
}
Adapter::~Adapter()
{
}
void Adapter::Request()
{
_ade->SpecificRequest();
}
//main.cpp
#include "Adapter.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
Adaptee* ade = new Adaptee;
Target* adt = new Adapter(ade);
adt->Request();
return 0;
}
Decorator 模式(装饰器模式)
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。
Component(被装饰对象的基类)
定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent(具体被装饰对象)
定义一个对象,可以给这个对象添加一些职责。
Decorator(装饰者抽象类)
维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。
ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
//装饰器模式:对类的功能进行扩展(代替继承)。可以动态的给类增加功能
//我们需要为英雄增加额外的功能或者属性,但是又不能影响Hero类,这个时候就可以考虑装饰器模式
#include <iostream>
using namespace std;
//抽象的英雄
class AbstractHero
{
public:
virtual void showStatus() = 0;
int mHp;
int mMp;
int mAt;
int mDf;
};
class HeroA:public AbstractHero
{
public:
HeroA()
{
mHp = 0;
mMp = 0;
mAt = 0;
mDf = 0;
}
virtual void showStatus()
{
cout << "血量:" << mHp << endl;
cout << "魔法:" << mMp << endl;
cout << "攻击:" << mAt << endl;
cout << "防御:" << mDf << endl;
}
};
//英雄穿上某个装饰物,那么也还是一个英雄
class AbstractEquipment: public AbstractHero
{
public:
AbstractEquipment(AbstractHero* hero)
{
this->pHero = hero;
}
virtual void showStatus()
{
}
AbstractHero* pHero;
};
//给英雄穿上狂徒铠甲
class KuangtuEquipment:public AbstractEquipment
{
public:
KuangtuEquipment(AbstractHero* hero):AbstractEquipment(hero){}
//增加额外的功能
void AddKuangtu()
{
cout << "英雄穿了狂徒之后" << endl;
this->mHp = this->pHero->mHp;
this->mMp = this->pHero->mMp;
this->mAt = this->pHero->mAt;
this->mDf = this->pHero->mDf+30;
}
virtual void showStatus()
{
AddKuangtu();
cout << "血量:" << mHp << endl;
cout << "魔法:" << mMp << endl;
cout << "攻击:" << mAt << endl;
cout << "防御:" << mDf << endl;
}
};
//无尽之刃
class Wujinzhireng:public AbstractEquipment
{
public:
Wujinzhireng(AbstractHero* hero) :AbstractEquipment(hero) {}
//增加额外的功能
void AddKuangtu()
{
cout << "英雄穿了无尽之刃后" << endl;
this->mHp = this->pHero->mHp;
this->mMp = this->pHero->mMp;
this->mAt = this->pHero->mAt+80;
this->mDf = this->pHero->mDf;
}
virtual void showStatus()
{
AddKuangtu();
cout << "血量:" << mHp << endl;
cout << "魔法:" << mMp << endl;
cout << "攻击:" << mAt << endl;
cout << "防御:" << mDf << endl;
}
};
void test01()
{
AbstractHero* hero = new HeroA;
hero->showStatus();
cout << "-------------------" << endl;
//给英雄穿上狂徒
hero = new KuangtuEquipment(hero);
hero->showStatus();
hero->mAt = 33;
//给英雄装备武器
hero = new Wujinzhireng(hero);
hero->showStatus();
}
int main()
{
test01();
}
总结:
Decorator 模式除了采用组合的方式取得了比采用继承方式更好的效果,Decorator 模式还给设计带来一种“即用即付”的方式来添加职责。在 OO 设计和分析经常有这样一种情况:为了多态,通过父类指针指向其具体子类,但是这就带来另外一个问题,这样处于高层的父类就承载了太多的特征(方法),并且继承自这个父类的所有子类都不可避免继承了父类的这些接口,但是可能这并不是这个具体子类所需要的。这样父类就太过臃肿了,那么当需要添加一个操作的时候就可以通过Decorator 模式来解决。
Composite 模式(组合实体模式)
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。
举例:
代码:
//Composite.h
#ifndef COMPOSITE_H
#define COMPOSITE_H
#include <list>
// 组合中的抽象基类
class Component
{
public:
Component(){}
virtual ~Component(){}
// 纯虚函数,只提供接口,没有默认的实现
virtual void Operation() = 0;
// 虚函数,提供接口,有默认的实现就是什么都不做
virtual void Add(Component* pChild);
virtual void Remove(Component* pChild);
virtual Component* GetChild(int nIndex);
};
// 派生自 Component,是其中的叶子组件的基类
class Leaf
: public Component
{
public:
Leaf(){}
virtual ~Leaf(){}
virtual void Operation();
};
// 派生自 Component,是其中的含有子件的组件的基类
class Composite
: public Component
{
public:
Composite(){}
virtual ~Composite();
virtual void Operation();
virtual void Add(Component* pChild);
virtual void Remove(Component* pChild);
virtual Component* GetChild(int nIndex);
private:
// 采用 list 容器去保存子组件
std::list<Component*> m_ListOfComponent;
};
#endif
//Composite.cpp
#include "Composite.h"
#include <iostream>
#include <algorithm>
/*-------------------------------------------------------------------
Component 成员函数的实现
-------------------------------------------------------------------*/
void Component::Add(Component* pChild)
{
}
void Component::Remove(Component* pChild)
{
}
Component* Component::GetChild(int nIndex)
{
return NULL;
}
/*-------------------------------------------------------------------
Leaf 成员函数的实现
-------------------------------------------------------------------*/
void Leaf::Operation()
{
std::cout << "Operation by leaf\n";
}
/*-------------------------------------------------------------------
Composite 成员函数的实现
-------------------------------------------------------------------*/
Composite::~Composite()
{
std::list<Component*>::iterator iter1, iter2, temp;
PDF created with pdfFactory trial version www.pdffactory.com
for (iter1 = m_ListOfComponent.begin(), iter2 = m_ListOfComponent.end();
iter1 != iter2;
)
{
temp = iter1;
++iter1;
delete (*temp);
}
}
void Composite::Add(Component* pChild)
{
m_ListOfComponent.push_back(pChild);
}
void Composite::Remove(Component* pChild)
{
std::list<Component*>::iterator iter;
iter = find(m_ListOfComponent.begin(), m_ListOfComponent.end(), pChild);
if (m_ListOfComponent.end() != iter)
{
m_ListOfComponent.erase(iter);
}
}
Component* Composite::GetChild(int nIndex)
{
if (nIndex <= 0 || nIndex > m_ListOfComponent.size())
return NULL;
std::list<Component*>::iterator iter1, iter2;
int i;
for (i = 1, iter1 = m_ListOfComponent.begin(), iter2 = m_ListOfComponent.
end();
iter1 != iter2;
++iter1, ++i)
{
if (i == nIndex)
break;
}
PDF created with pdfFactory trial version www.pdffactory.com
return *iter1;
}
void Composite::Operation()
{
std::cout << "Operation by Composite\n";
std::list<Component*>::iterator iter1, iter2;
for (iter1 = m_ListOfComponent.begin(), iter2 = m_ListOfComponent.end();
iter1 != iter2;
++iter1)
{
(*iter1)->Operation();
}
}
#include "Composite.h"
#include <stdlib.h>
int main()
{
Leaf *pLeaf1 = new Leaf();
Leaf *pLeaf2 = new Leaf();
Composite* pComposite = new Composite;
pComposite->Add(pLeaf1);
pComposite->Add(pLeaf2);
pComposite->Operation();
pComposite->GetChild(2)->Operation();
delete pComposite;
Flyweight 模式(享元模式)
如果一个应用程序使用了太多的对象,就会造成很大的存储开销。特别是对于大量轻量级(细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费。例如一个字母“a”在文档中出现了100000 次,而实际上我们可以让这一万个字母“a”共享一个对象,当然因为在不同的位置可能字母“a”有不同的显示效果(例如字体和大小等设置不同),在这种情况我们可以为将对象的状态分为“外部状态”和“内部状态”,将可以被共享(不会变化)的状态作为内部状态存储在对象中,而外部对象(例如上面提到的字体、大小等)我们可以在适当的时候将外部对象最为参数传递给对象(例如在显示的时候,将字体、大小等信息传递给对象)。
Flyweight 模式中有一个类似 Factory 模式的对象构造工厂FlyweightFactory,当客户程序员(Client)需要一个对象时候就会向 FlyweightFactory 发出请求对象的消息 GetFlyweight()消息,FlyweightFactory 拥有一个管理、存储对象的“仓库”(或者叫对象池,vector 实现),GetFlyweight()消息会遍历对象池中的对象,如果已经存在则直接返回给 Client,否则创建一个新的对象返回给 Client。
代码:
//Flyweight.h
#ifndef FLYWEIGHT_H
#define FLYWEIGHT_H
#include <string>
#include <list>
typedef std::string STATE;
class Flyweight
{
public:
virtual ~Flyweight(){}
STATE GetIntrinsicState();
virtual void Operation(STATE& ExtrinsicState) = 0;
protected:
Flyweight(const STATE& state)
:m_State(state)
{
}
private:
STATE m_State;
};
class FlyweightFactory
{
public:
FlyweightFactory(){}
~FlyweightFactory();
Flyweight* GetFlyweight(const STATE& key);
private:
std::list<Flyweight*> m_listFlyweight;
};
class ConcreateFlyweight
: public Flyweight
{
public:
ConcreateFlyweight(const STATE& state)
: Flyweight(state)
{
}
virtual ~ConcreateFlyweight(){}
virtual void Operation(STATE& ExtrinsicState);
};
#endif
//Flyweight.cpp
#include "FlyWeight.h"
#include <iostream>
inline STATE Flyweight::GetIntrinsicState()
{
return m_State;
}
FlyweightFactory::~FlyweightFactory()
{
std::list<Flyweight*>::iterator iter1, iter2, temp;
for (iter1 = m_listFlyweight.begin(), iter2 = m_listFlyweight.end();
iter1 != iter2;
)
{
temp = iter1;
++iter1;
delete (*temp);
}
m_listFlyweight.clear();
}
Flyweight* FlyweightFactory::GetFlyweight(const STATE& key)
{
std::list<Flyweight*>::iterator iter1, iter2;
for (iter1 = m_listFlyweight.begin(), iter2 = m_listFlyweight.end();
iter1 != iter2;
++iter1)
{
if ((*iter1)->GetIntrinsicState() == key)
{
std::cout << "The Flyweight:" << key << " already exits"<< std::endl;
return (*iter1);
}
}
std::cout << "Creating a new Flyweight:" << key << std::endl;
Flyweight* flyweight = new ConcreateFlyweight(key);
m_listFlyweight.push_back(flyweight);
}
void ConcreateFlyweight::Operation(STATE& ExtrinsicState)
{
}
#include "FlyWeight.h"
int main()
{
FlyweightFactory flyweightfactory;
flyweightfactory.GetFlyweight("hello");
flyweightfactory.GetFlyweight("world");
flyweightfactory.GetFlyweight("hello");
system("pause");
return 0;
}
**适用性:**一个应用程序使用了大量对象;完全由于使用大量的对象造成很大的存储开销;对象的大多数状态都可变为外部状态;如果删除对象的外部状态,那么可以使用相对较少的共享对象取代外部对象。
Facade 模式(外观模式)
外观模式是一个很重要、平常也用得很多的一个设计模式
实际上在软件系统开发中也经常回会遇到这样的情况,可能你实现了一些接口(模块),而这些接口(模块)都分布在几个类中(比如 A 和 B、C、D):A 中实现了一些接口,B 中实现一些接口(或者 A 代表一个独立模块,B、C、D 代表另一些独立模块)。然后你的客户程序员(使用你设计的开发人员)只有很少的要知道你的不同接口到底是在那个类中实现的,绝大多数只是想简单的组合你的 A-D 的类的接口,他并不想知道这些接口在哪里实现的。
下面举一个KTV的例子。假如KTV里面有电视机、电灯、音响、DVD、游戏机这些设备。平常KTV包厢里面没人时电灯都是打开的。电视机、音响、游戏机、DVD都是关闭的。当KTV里面有人了那么电灯关闭,其他东西打开。如果要一个一个开和关特别麻烦。这时候就可以使用外观模式,定义一个总开关。
代码:
//外观模式
#include <iostream>
using namespace std;
//电视机类
class Television {
public:
void on() {
cout << "电视机打开" << endl;
}
void off() {
cout << "电视机关闭" << endl;
}
};
//灯类
class Light {
public:
void on() {
cout << "灯打开" << endl;
}
void off() {
cout << "灯关闭" << endl;
}
};
//音响
class Audio {
public:
void on() {
cout << "音响打开" << endl;
}
void off() {
cout << "音响关闭" << endl;
}
};
//麦克风
class Microphone {
public:
void on() {
cout << "麦克风打开" << endl;
}
void off() {
cout << "麦克风关闭" << endl;
}
};
//DVD
class DVDplayer {
public:
void on() {
cout << "DVD打开" << endl;
}
void off() {
cout << "DVD关闭" << endl;
}
};
//游戏机
class GameMachine {
public:
void on() {
cout << "游戏机打开" << endl;
}
void off() {
cout << "游戏机关闭" << endl;
}
};
//外观模式
class KTVMode {
public:
KTVMode() {
pTV = new Television;
pLight = new Light;
pAudio = new Audio;
pMicrophone = new Microphone;
pDvd = new DVDplayer;
}
void onKTV() {
pTV->on();
pLight->off();
pAudio->on();
pMicrophone->on();
pDvd->on();
}
void offKTV() {
pTV->off();
pLight->off();
pAudio->off();
pMicrophone->off();
pDvd->off();
}
~KTVMode() {
delete pTV;
delete pLight;
delete pAudio;
delete pMicrophone;
delete pDvd;
}
private:
Television* pTV;
Light* pLight;
Audio* pAudio;
Microphone* pMicrophone;
DVDplayer* pDvd;
};
void test01() {
KTVMode* ktv = new KTVMode;
//KTV包厢来人了
ktv->onKTV();
}
int main()
{
test01();
}
Proxy 模式(代理模式)
代理模式(Proxy Pattern)是指为其他对象提供一种代理,以控制对这个对象的访问。 代理对象在客服端和目标对象之间起到中介作用。
在生活中,我们经常见到这样的场景,如:租房中介、售票黄牛、婚介、经纪人、快递、 事务代理、非侵入式日志监听等,这些都是代理模式的实际体现
注意事项:
1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
举例:
现在有一个操作系统,只需要调用run()就可以启动操作系统,但是进入操作系统之前必须要进行账户名和密码的认证。认证成功后这个代理才会让你进入操作系统,其中认证的这个过程就是一个代理。
代码:
//代理模式:提供一种代理来控制其他对象的访问
#include <iostream>
using namespace std;
class AbstractCommonInterface {
public:
virtual void run() = 0;
};
//下面是操作系统类
class MySystem :public AbstractCommonInterface{
public:
virtual void run() {
cout << "系统启动" << endl;
}
};
//代理: 启动系统必须要权限验证,不是所以的人都可以来启动我的系统,必须要提供用户名和密码
class MySystemProxy :public AbstractCommonInterface {
public:
MySystemProxy(string userName, string password) {
this->mUserName = userName;
this->mPassword = password;
pMySystem = new MySystem;
}
bool checkUserNameAndPassword() {
if (mUserName == "admin" && mPassword == "admin") {
return true;
}
return false;
}
virtual void run() {
if (checkUserNameAndPassword() == true) {
cout << "启动成功" << endl;
this->pMySystem->run();
}
else {
cout << "用户名或者密码错误,权限不足" << endl;
}
}
~MySystemProxy() {
if (pMySystem != NULL) {
delete pMySystem;
}
}
private:
string mUserName;
string mPassword;
MySystem* pMySystem;
};
void test01() {
MySystemProxy* proxy = new MySystemProxy("admin", "admin");
proxy->run();
}
int main()
{
test01();
}
总结:
1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制
三、行为模式
Template 模式(模板模式)
在面向对象系统的分析与设计过程中经常会遇到这样一种情况:对于某一个业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用算法)是相同的。Template 提供了这种情况的一个实现框架。Template 模式是采用继承的方式实现这一点:将逻辑(算法)框架放在抽象基类中,并定义好细节的接口,子类中实现细节。
举例:
假如我们要做饮料,那么我们首先会定义一个做饮料的模板,即煮水->冲泡->导入杯中->加辅助材料,具体煮什么水、冲泡什么东西放到子类中实现。然后定义一个模板方法,当我们要做饮料时就调用这个方法即可。
代码:
//模板方法模式:在父类中定义一个方法的抽象,由它的子类来实现细节的处理,在子类实现详细的处理算法时并不会改变算法中的执行次序。
#include <iostream>
using namespace std;
//做饮料模板
class DrinkTemplate {
public:
//煮水
virtual void BoildWater() = 0;
//冲泡
virtual void Brew() = 0;
//倒入杯中
virtual void PourInCup() = 0;
//加辅助材料
virtual void AddSomething() = 0;
//模板方法
void Make() {
BoildWater();
Brew();
PourInCup();
AddSomething();
}
};
//做咖啡: 实现做饮料模板
class Coffee :public DrinkTemplate {
virtual void BoildWater() {
cout << "煮山泉水" << endl;
}
virtual void Brew() {
cout << "冲泡咖啡" << endl;
}
virtual void PourInCup() {
cout << "咖啡倒入杯中" << endl;
}
virtual void AddSomething() {
cout << "加糖,加牛奶" << endl;
}
};
//做茶: 实现做饮料模板
class Tea :public DrinkTemplate {
virtual void BoildWater() {
cout << "煮自来水" << endl;
}
virtual void Brew() {
cout << "冲泡铁观音" << endl;
}
virtual void PourInCup() {
cout << "茶水倒入杯中" << endl;
}
virtual void AddSomething() {
cout << "加糖,加大蒜" << endl;
}
};
void test01() {
Tea* tea = new Tea;
tea->Make();
Coffee* coffee = new Coffee;
coffee->Make();
}
int main()
{
test01();
}
总结:
- 可以看到Template模式获得一种反向控制结构效果,这也是面向对象系统的分析和设计中一个原则,依赖倒置原则。其含义就是父类调用子类的操作(高层模块调用低层模块的操作),低层模块实现高层模块声明的接口。这样控制权在父类(高层模块),低层模块反而要依赖高层模块。
- 但是也有局限性,因为模板模式采用继承这种强制约束关系,导致复用性不高。假如我接下来要做冷饮,那么Make函数中的煮水操作其实可以省去 直接冲泡即可,但是我们就无法再次使用这个模板了,这样就导致了不能复用子类的实现步骤。
Strategy 模式(策略模式)
Strategy 模式和 Template 模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。
简而言之,Strategy 模式是对算法的封装。处理一个问题的时候可能有多种算法,这些算法的接口(输入参数,输出参数等)都是一致的,那么可以考虑采用Strategy 模式对这些算法进行封装,在基类中定义一个函数接口就可以了。
举例:
假如现在有个英雄需要使用武器对付敌人,武器有两种匕首和AK,那么这么选择使用哪吧武器其实就是一种策略了那么就可以将策略模式分为三部分:
- Strategy 策略基类 (抽象武器)
- ConcreteStrategy 具体策略 (使用匕首或AK)
- Context 具体使用策略的对象(英雄)
代码:
//策略模式
#include <iostream>
using namespace std;
//抽象武器 策略基类(抽象的策略)
class WeaponStrategy {
public:
virtual void UseWeapon() = 0;
};
//具体的策略使用匕首作为武器
class Knife :public WeaponStrategy {
public:
virtual void UseWeapon() {
cout << "使用匕首" << endl;
}
};
//具体的策略使用AK47作为武器
class AK47 :public WeaponStrategy {
public:
virtual void UseWeapon() {
cout << "使用AK47" << endl;
}
};
//具体使用策略的角色
class Character {
public:
WeaponStrategy* pWeapon;
void setWeapon(WeaponStrategy* pWeapon) {
this->pWeapon = pWeapon;
}
void ThrowWeapon() {
this->pWeapon->UseWeapon();
}
};
void test01() {
Character* character = new Character;
WeaponStrategy* knife = new Knife;
WeaponStrategy* ak47 = new AK47;
//用匕首当作武器
character->setWeapon(knife);
character->ThrowWeapon();
character->setWeapon(ak47);
character->ThrowWeapon();
delete ak47;
delete knife;
delete character;
}
int main()
{
test01();
}
总结:
可以看到Character 类是通过组合WeaponStrategy 的形式调用武器使用的,这就比继承要好得多,因为在面向对象的设计中的有一条很重要的原则就是:优先使用对象组合,而非类继承。
优点:
- 算法可以自由切换。
- 避免使用多重条件判断。
- 扩展性良好。
缺点:
- 策略类会增多。
- 所有策略类都需要对外暴露
State 模式(状态模式)
每个人、事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)。
主要解决:
- 当状态数目不是很多的时候,Switch/Case 可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组的Switch/Case 语句将是一件异常困难并且容易出错的事情。
- 状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
举例:
以抽奖活动为例。
有四个状态:
- 【抽奖状态】
- 【可以抽奖】
- 【发放奖品】
- 【奖品已领完】
抽奖活动的初始状态为【不能抽奖】状态,在扣除50积分后进入【可以抽奖】状态,【可以抽奖】状态有90%的几率不中奖,不中奖就会回到【抽奖状态】,10%的几率中奖,中奖后进入【发放奖品】状态,当奖品数量为0是进入【奖品已领完】状态。如果用switch/if实现起来比较复杂下面用状态模式实现。
UML图
//State.h
#pragma once
class State
{
public:
//扣除50积分
virtual void deductMeney() = 0;
//是否中奖
virtual bool raffle() = 0;
//发放奖品
virtual void dispensePrize() = 0;
};
//CanRaffleState.h
#pragma once
#include <iostream>
#include "State.h"
using namespace std;
class RaffleActivity;
//能抽奖状态
class CanRaffleState :public State
{
public:
CanRaffleState(RaffleActivity* activity);
//已经扣除积分了,不能再扣了
void deductMeney() override;
//可以抽奖,根据抽奖情况改成新的状态
bool raffle() override;
void dispensePrize() override;
private:
RaffleActivity* activity;
};
//CanRaffleState.cpp
#include "CanRaffleState.h"
#include "RaffleActivity.h"
CanRaffleState::CanRaffleState(RaffleActivity* activity)
{
srand(time(NULL));
this->activity = activity;
}
//已经扣除积分了,不能再扣了
void CanRaffleState::deductMeney()
{
cout << "已经扣过积分了" << endl;
}
//可以抽奖,根据抽奖情况改成新的状态
bool CanRaffleState::raffle()
{
cout << "正在抽奖" << endl;
int result = rand() % 10;
if (0 == result)
{
//将activity的状态设置为发放奖品的状态
activity->setState(activity->getDispenseState());
return true;
}
else
{
cout << "很遗憾没有抽中奖品" << endl;
//将activity的状态设置为不能抽奖的状态
activity->setState(activity->getNoRaffleState());
return false;
}
}
void CanRaffleState::dispensePrize()
{
cout << "没中奖,不能发放奖品" << endl;
}
//RaffleActivity.h
#pragma once
#include "State.h"
class RaffleActivity;
//奖品发送完毕状态
class DispenseOutState :public State
{
public:
DispenseOutState(RaffleActivity* activity);
void deductMeney() override;
bool raffle() override;
//发放奖品
void dispensePrize() override;
private:
RaffleActivity* activity;
};
//RaffleActivity.cpp
#include "DispenseOutState.h"
#include "RaffleActivity.h"
DispenseOutState::DispenseOutState(RaffleActivity* activity)
{
this->activity = activity;
}
void DispenseOutState::deductMeney()
{
cout << "奖品已经发完了,请下次再参加" << endl;
}
bool DispenseOutState::raffle()
{
cout << "奖品已经发完了,请下次再参加" << endl;
return false;
}
//发放奖品
void DispenseOutState::dispensePrize()
{
cout << "奖品已经发完了,请下次再参加" << endl;
}
//RaffleActivity.h
#pragma once
#include <iostream>
#include "State.h"
using namespace std;
class RaffleActivity;
//发放奖品的状态
class DispenseState :public State
{
public:
DispenseState(RaffleActivity* activity);
void deductMeney() override;
bool raffle() override;
//发放奖品
void dispensePrize() override;
private:
RaffleActivity* activity;
};
//DispenseState.cpp
#include "DispenseState.h"
#include "RaffleActivity.h"
DispenseState::DispenseState(RaffleActivity* activity)
{
this->activity = activity;
}
void DispenseState::deductMeney()
{
cout << "不能扣除积分" << endl;
}
bool DispenseState::raffle()
{
cout << "不能抽奖了" << endl;
return false;
}
//发放奖品
void DispenseState::dispensePrize()
{
if (activity->getCount() > 0)
{
cout << "送出奖品" << endl;
activity->setState(activity->getNoRaffleState());
}
else
{
cout << "很遗憾,奖品发完了" << endl;
//奖品已经发完,后面的人就不能抽奖了
activity->setState(activity->getDispenseOutState());
}
}
//RaffleActivity.h
#pragma once
#include <iostream>
#include "State.h"
using namespace std;
class RaffleActivity;
//不能抽奖状态
class NoRaffleState :public State
{
public:
NoRaffleState(RaffleActivity* activity);
//在不能抽奖状态是可以扣积分的,扣除积分后设置成可以抽奖状态
void deductMeney() override;
bool raffle() override;
void dispensePrize() override;
private:
//初始化时传入活动,扣除积分后改变其状态
RaffleActivity* activity;
};
//NoRaffleState.cpp
#include "NoRaffleState.h"
#include "RaffleActivity.h";
NoRaffleState::NoRaffleState(RaffleActivity* activity)
{
this->activity = activity;
}
//在不能抽奖状态是可以扣积分的,扣除积分后设置成可以抽奖状态
void NoRaffleState::deductMeney()
{
std::cout << "扣除50积分,可以抽奖了" << endl;
activity->setState(activity->getCanRaffleState());
}
bool NoRaffleState::raffle()
{
cout << "扣了积分才能抽奖" << endl;
return false;
}
void NoRaffleState::dispensePrize()
{
cout << "不能发奖品" << endl;
}
//RaffleActivity.h
#pragma once
#include "CanRaffleState.h"
#include "DispenseOutState.h"
#include "DispenseState.h"
#include "State.h"
#include "NoRaffleState.h"
class RaffleActivity
{
public:
State* getState() const{
return state;
}
void setState(State* const state)
{
this->state = state;
}
int getCount()
{
return count--;
}
void setCount(const int count)
{
this->count = count;
}
State* getNoRaffleState() const
{
return noRaffleState;
}
void setNoRaffleState(State* const noRaffleState)
{
this->noRaffleState = noRaffleState;
}
State* getCanRaffleState() const
{
return canRaffleState;
}
void setCanRaffleState(State* const canRaffleState)
{
this->canRaffleState = canRaffleState;
}
State* getDispenseState() const
{
return dispenseState;
}
void setDispenseState(State* const dispenseState)
{
this->dispenseState = dispenseState;
}
State* getDispenseOutState() const
{
return dispenseOutState;
}
void setDispenseOutState(State* const dispenseOutState)
{
this->dispenseOutState = dispenseOutState;
}
RaffleActivity(int count)
{
//初始化当前状态为 不能抽奖状态
this->state = getNoRaffleState();
//初始化奖品数量
this->count = count;
}
//扣分,调用当前状态的deductMoney
void deductMoney()
{
state->deductMeney();
}
//抽奖
void raffle()
{
//如果抽中奖了,则领奖品
if (state->raffle())
{
state->dispensePrize();
}
}
private:
//state表示活动当前的状态
State* state = nullptr;
//奖品数量
int count = 0;
//四个属性 表示四种状态
State* noRaffleState = new NoRaffleState(this);
State* canRaffleState = new CanRaffleState(this);
State* dispenseState = new DispenseState(this);
State* dispenseOutState = new DispenseOutState(this);
};
//main.cpp
#include <iostream>
#include "RaffleActivity.h"
using namespace std;
int main(int argc, char* argv[])
{
RaffleActivity* activity = new RaffleActivity(1);
for(int i=0;i<50;i++)
{
cout << "第" << i << "次抽奖" << endl;
activity->deductMoney();
activity->raffle();
cout << endl;
}
return 0;
}
优点:
- 封装了转换规则。
- 枚举可能的状态,在枚举状态之前需要确定状态种类。
- 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
Observer 模式(观察者模式)
Observer 模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变。最常见的一个例子就是:对同一组数据进行统计分析时候,我们希望能够提供多种形式的表示(例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
举例:
Subject 提供依赖于它的观察者 Observer 的注册(registerObserver)和注销(remove)操作,并且提供了使得依赖于它的所有观察者同步的操作(notifyObserver),观察者 Observer 则提供一个 Update 操作,注意这里的 Observer 的 Update 操作并不在 Observer 改变了 Subject 目标状态的时候就对自己进行更新,这个更新操作要延迟到 Subject 对象发出 Notify 通知所有Observer 进行修改(调用 Update)。
#include <iostream>
#include <list>
using namespace std;
//抽象的英雄 抽象的观察者 Observer
class AbstractHero {
public:
virtual void Update() = 0;
};
//具体的英雄 具体的观察者
class HeroA :public AbstractHero {
public:
HeroA() {
cout << "英雄A正在鲁BOSS" << endl;
}
virtual void Update() {
cout << "英雄A停止撸,待机状态" << endl;
}
};
class HeroB :public AbstractHero {
public:
HeroB() {
cout << "英雄B正在鲁BOSS" << endl;
}
virtual void Update() {
cout << "英雄B停止撸,待机状态" << endl;
}
};
class HeroC :public AbstractHero {
public:
HeroC() {
cout << "英雄C正在鲁BOSS" << endl;
}
virtual void Update() {
cout << "英雄C停止撸,待机状态" << endl;
}
};
class HeroD :public AbstractHero {
public:
HeroD() {
cout << "英雄D正在鲁BOSS" << endl;
}
virtual void Update() {
cout << "英雄D停止撸,待机状态" << endl;
}
};
class HeroE :public AbstractHero {
public:
HeroE() {
cout << "英雄E正在鲁BOSS" << endl;
}
virtual void Update() {
cout << "英雄E停止撸,待机状态" << endl;
}
};
//定义抽象的观察目标 Subject
class AbstractBoss {
public:
// 添加观察者
virtual void addHero(AbstractHero* hero) = 0;
// 删除观察者
virtual void deleteHero(AbstractHero* hero) = 0;
// 通知所有观察者
virtual void notifv() = 0;
};
//相对于 concreteSubject
class BOSSA :public AbstractBoss {
public:
// 添加观察者
virtual void addHero(AbstractHero* hero) {
pHeroList.push_back(hero);
}
// 删除观察者
virtual void deleteHero(AbstractHero* hero) {
pHeroList.remove(hero);
}
// 通知所有观察者
virtual void notifv() {
for (list<AbstractHero*>::iterator it = pHeroList.begin(); it != pHeroList.end(); it++) {
(*it)->Update();
}
}
list<AbstractHero*> pHeroList;
};
void test01() {
// 创建观察者
AbstractHero* heroA = new HeroA;
AbstractHero* heroB = new HeroB;
AbstractHero* heroC = new HeroC;
AbstractHero* heroD = new HeroD;
AbstractHero* heroE = new HeroE;
// 创建观察目标
AbstractBoss* bossA = new BOSSA;
bossA->addHero(heroA);
bossA->addHero(heroB);
bossA->addHero(heroC);
bossA->addHero(heroD);
bossA->addHero(heroE);
cout << "heroC阵亡" << endl;
bossA->deleteHero(heroC);
cout << "Boss死了,通知其他英雄停止攻击。。。" << endl;
bossA->notifv();
}
int main() {
test01();
return 0;
}
优点:
- 观察者和被观察者是抽象耦合的。
- 建立一套触发机制。
缺点:
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
Memento 模式(备忘录模式)
没有人想犯错误,但是没有人能够不犯错误。犯了错误一般只能改过,却很难改正(恢复)。世界上没有后悔药,但是我们在进行软件系统的设计时候是要给用户后悔的权利(实际上可能也是用户要求的权利:)),我们对一些关键性的操作肯定需要提供诸如撤销(Undo)的操作。那这个后悔药就是 Memento 模式提供的。
Memento 模式的关键就是要在不破坏封装行的前提下,捕获并保存一个类的内部状态,这样就可以利用该保存的状态实施恢复操作。
示例:
代码:
- Originator 原始的状态
- Memento 包含了要被恢复的对象的状态
- Caretaker 用于保存所有的memento的一个管理类
我们有一个英雄,有一个初始状态,在收到敌人的DEBUFF后状态会下降,在DEBUFF时间过了之后又会回到正常的状态,所以我们需要保存正常状态的值,这个时候我们就可以采用备忘录模式了。
//Originator.h
#ifndef _ORIGINATOR_H_
#define _ORIGINATOR_H_
#include <string>
#include "Memento.h"
class Originator
{
public:
std::string getState() const
{
return state;
}
void setState(const std::string& state)
{
this->state = state;
}
//保存一个状态对象Memento 即用当前状态的值去创建一个Memento然后将其返回
Memento SaveStateMemento()
{
return Memento(state);
}
//通过备忘录对象,恢复状态
void getStateFromMemento(Memento memento)
{
state = memento.getState();
}
private:
std::string state;
};
#endif
//Memento.h
#ifndef _MEMENTO_H_
#define _MEMENTO_H_
#include <string>
class Memento
{
public:
explicit Memento(const std::string& state)
: state(state)
{
}
std::string getState() const
{
return state;
}
private:
std::string state;
};
#endif
//Caretaker.h
#ifndef _CARETAKER_H_
#define _CARETAKER_H_
#include <vector>
#include "Memento.h"
class Caretaker
{
public:
void add(Memento memento)
{
mementoList.push_back(memento);
}
//获取到第index个Originator的备忘录对象(即备忘录状态)
Memento get(int index)
{
return mementoList[index];
}
private:
//在mementoList中会有很多备忘录对象
std::vector<Memento> mementoList;
};
#endif
//main.h
#include <iostream>
#include "Originator.h"
#include "Caretaker.h"
using namespace std;
int main(int argc, char* argv[])
{
Originator originator;
Caretaker caretaker;
originator.setState("状态1,攻击力为100");
//保存当前状态
caretaker.add(originator.SaveStateMemento());
//受到debuff,攻击力下降
originator.setState("状态2,攻击力为80");
//保存状态
caretaker.add(originator.SaveStateMemento());
cout << "当前的状态:" << originator.getState()<<endl;
cout << "debuff时间结束,回到状态1" << endl;
originator.getStateFromMemento(caretaker.get(0));
cout << "恢复到状态1后的状态:" << originator.getState();
return 0;
}
总结:
优点:
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
Mediator 模式(中介者模式)
在面向对象系统的设计和开发过程中,对象之间的交互和通信是最为常见的情况,因为对象间的交互本身就是一种通信。在系统比较小的时候,可能对象间的通信不是很多、对象也比较少,我们可以直接硬编码到各个对象的方法中。但是当系统规模变大,对象的量变引起系统复杂度的急剧增加,对象间的通信也变得越来越复杂,这时候我们就要提供一个专门处理对象间交互和通信的类,这个中介者就是 Mediator 模式。所以Mediator 模式的实现关键就是将对象 Colleague 之间的通信封装到一个类种单独处理。
举例:
说明:
- Meadiator是抽象中介者,定义了同事对象到中介者对象的接口。
- Colleague是抽象同事类
- ConcreteMediator是具体的中介者对象,实现抽象方法,他需要知道所有的具体的同事类,用一个集合来管理它们,并接收某个同事的消息,完成相应的任务。
- ConcreteColleague是具体的同事类,会有很多,每个同事只知道自己的行为,而不了解其他同事的行为,它们都依赖中介者对象。
中介者模式的目标:
将网状结构改成星状结构
使用中介者模式后
举例:
代码:
//Colleague.h
#pragma once
#include <string>
class Mediator;
class Colleague
{
public:
Mediator* getMediator();
void setMediator(Mediator* const mediator);
Colleague(Mediator* mediator);
virtual void Notify(std::string message)=0;
private:
Mediator* mediator;
};
//Colleague.cpp
#include "Colleague.h"
#include "Mediator.h"
Mediator* Colleague::getMediator()
{
return mediator;
}
void Colleague::setMediator(Mediator* const mediator)
{
this->mediator = mediator;
}
Colleague::Colleague(Mediator* mediator)
{
this->mediator = mediator;
this->mediator->add(this);
}
//ConcreteColleague1.h
#ifndef _CONCRETECOLLEAGUE1_H_
#define _CONCRETECOLLEAGUE1_H_
#include <iostream>
#include <string>
#include "Colleague.h"
#include "Mediator.h"
class ConcreteColleague1:public Colleague
{
public:
ConcreteColleague1(Mediator* mediator):Colleague(mediator)
{
}
void send(std::string message)
{
getMediator()->send(message, this);
}
void Notify(std::string message)
{
std::cout << "同事1收到消息:" + message<<std::endl;
}
};
#endif
//ConcreteColleague2.h
#ifndef _CONCRETECOLLEAGUE2_H_
#define _CONCRETECOLLEAGUE2_H_
#include <iostream>
#include <string>
#include "Colleague.h"
#include "Mediator.h"
class ConcreteColleague2 :public Colleague
{
public:
ConcreteColleague2(Mediator* mediator) :Colleague(mediator)
{
}
void send(std::string message)
{
getMediator()->send(message, this);
}
void Notify(std::string message)
{
std::cout << "同事2收到消息:" + message << std::endl;
}
};
#endif
//ConcreteMediator.h
#ifndef _CONCRETEMEDIATOR_H_
#define _CONCRETEMEDIATOR_H_
#include <vector>
#include "Colleague.h"
#include "Mediator.h"
class ConcreteMediator:public Mediator
{
public:
void add(Colleague* colleague)
{
colleaguesList.push_back(colleague);
}
void send(std::string message, Colleague* colleague)
{
for (auto value : colleaguesList)
{
if(value!=colleague)
{
value->Notify(message);
}
}
}
private:
std::vector<Colleague*> colleaguesList;
};
#endif
//Mediator.h
#ifndef _MEDIATOR_H_
#define _MEDIATOR_H_
#include <string>
#include "Colleague.h"
class Mediator
{
public:
virtual void send(std::string message, Colleague* colleague)=0;
virtual void add(Colleague* colleague)=0;
};
#endif
//main.cpp
#include <iostream>
#include "ConcreteMediator.h"
#include "ConcreteColleague1.h"
#include "ConcreteColleague2.h"
#include "Mediator.h"
#include "Colleague.h"
using namespace std;
int main(int argc, char* argv[])
{
Mediator* mediator = new ConcreteMediator();
ConcreteColleague1* colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2* colleague2 = new ConcreteColleague2(mediator);
colleague1->send("早上好啊!");
colleague2->send("早安!");
return 0;
}
总结:
- 何时使用:多个类相互耦合,形成了网状结构,需要将上述网状结构分离为星型结构的时候。
- 中介者承担了较多的责任,一旦中介者出现问题,整个系统都会收到影响。
Command 模式(命令模式)
- 请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
- Command 模式的最终目的就是将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
举例:
- Invoker 是调用者角色
- Command 是命令角色,需要执行的所有命令都在这里
- Receiver 是接受者角色,知道如何实施和执行一个请求相关的操作
- ConcreteCommand 将接受者对象与一个动作绑定,调用接受者相应的操作
假如现在有一个游戏服务器,该游戏服务器一共可以处理四种不同的请求:处理增加金币、处理增加钻石、处理玩家装备、玩家升级请求。我们需要把这些请求封装成对象,从而加入请求队列一次进行处理。
代码:
//命令模式
#include <iostream>
#include <queue>
#include <Windows.h>
using namespace std;
//协议处理类 (请求类)
class HandleClinetProtocal
{
public:
//处理增加金币
void AddMoney()
{
cout << "给玩家增加金币" << endl;
}
//处理增加钻石
void AddDiamond()
{
cout << "给玩家增加钻石" << endl;
}
//处理玩家装备
void AddEquipment()
{
cout << "给玩家穿装备" << endl;
}
//玩家升级
void AddLevel()
{
cout << "给玩家升级" << endl;
}
};
//命令接口
class AbstractCommand
{
public:
//相当于execute()
virtual void handle() = 0;
};
//下面是把每个功能都封装为一个请求对象
//处理增加金币请求
class AddMoneyCommand:public AbstractCommand
{
public:
AddMoneyCommand(HandleClinetProtocal* protocal)
{
this->pProtocol = protocal;
}
virtual void handle()
{
this->pProtocol->AddMoney();
}
public:
HandleClinetProtocal* pProtocol;
};
//处理增加钻石请求
class AddDiamondCommand :public AbstractCommand
{
public:
AddDiamondCommand(HandleClinetProtocal* protocal)
{
this->pProtocol = protocal;
}
virtual void handle()
{
this->pProtocol->AddDiamond();
}
public:
HandleClinetProtocal* pProtocol;
};
//处理玩家装备请求
class AddEquipmentCommand :public AbstractCommand
{
public:
AddEquipmentCommand(HandleClinetProtocal* protocal)
{
this->pProtocol = protocal;
}
virtual void handle()
{
this->pProtocol->AddEquipment();
}
public:
HandleClinetProtocal* pProtocol;
};
//处理玩家升级请求
class AddLevelCommand :public AbstractCommand
{
public:
AddLevelCommand(HandleClinetProtocal* protocal)
{
this->pProtocol = protocal;
}
virtual void handle()
{
this->pProtocol->AddLevel();
}
public:
HandleClinetProtocal* pProtocol;
};
//服务器程序 (命令调用类)
class Server
{
public:
//将请求对象放入处理队列
void addRequest(AbstractCommand* command)
{
mCommands.push(command);
}
//启动处理程序
void startHandle()
{
while (!mCommands.empty())
{
Sleep(2000);
AbstractCommand* command = mCommands.front();
command->handle();
mCommands.pop();
}
}
queue<AbstractCommand*> mCommands;
};
void test01()
{
HandleClinetProtocal* protocal = new HandleClinetProtocal;
//客户端增加金币的请求
AbstractCommand* addmoney = new AddMoneyCommand(protocal);
//客户端增加钻石的请求
AbstractCommand* adddiamond = new AddDiamondCommand(protocal);
//客户端穿装备的请求
AbstractCommand* addequipment = new AddEquipmentCommand(protocal);
//客户端升级请求
AbstractCommand* addlevel = new AddLevelCommand(protocal);
//将客户端的请求加入到请求队列中
Server* server = new Server;
server->addRequest(addmoney);
server->addRequest(adddiamond);
server->addRequest(addequipment);
server->addRequest(addlevel);
//服务器开始处理请求
server->startHandle();
}
int main()
{
test01();
return 0;
}
Visitor 模式(访问者模式)
- 访问者模式(VisitorPattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
- 主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
- 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口
- 访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决
举例:
现在有一场演出,将参加选手分为男人和女人,当看完某个歌手表演后,需要对他们进行评价(失败、成功等)
代码:
#include <iostream>
#include <list>
class Man;
class Woman;
using namespace std;
class Action
{
public:
virtual void getManResult(Man* man) =0;
virtual void getWomanResult(Woman* woman) =0;
private:
};
class Success:public Action
{
public:
void getManResult(Man* man) override
{
cout << "男人的给的评价该歌手是很成功" << endl;
}
void getWomanResult(Woman* woman) override
{
cout << "女人的给的评价该歌手是很成功" << endl;
}
};
class Fail :public Action
{
public:
void getManResult(Man* man) override
{
cout << "男人的给的评价该歌手是失败" << endl;
}
void getWomanResult(Woman* woman) override
{
cout << "女人的给的评价该歌手是失败" << endl;
}
};
class Person
{
public:
//提供一个方法让访问者可以访问
virtual void accept(Action* action) = 0;
};
//这里用用到了双分派,即首先在客户端程序中,将具体的状态作为参数传递进Man中(第一次分派)
//然后在Man中调用作为参数的“具体方法” getManResult同时将自己作为参数传入完成第二次分派
class Man:public Person
{
public:
void accept(Action* action) override
{
action->getManResult(this);
}
};
class Woman: public Person
{
public:
void accept(Action* action) override
{
action->getWomanResult(this);
}
};
//数据结构,管理很多人(Man、Woman)用来装符合某一个评价的所有人
class ObjectStructure
{
public:
//添加到list
void attach(Person* p)
{
persons.push_back(p);
}
//移除
void detach(Person* p)
{
persons.remove(p);
delete p;
}
//显示测评情况
void display(Action* action)
{
for (auto value : persons)
{
value->accept(action);
}
}
private:
list<Person*> persons;
};
int main(int argc, char* argv[])
{
//创建ObjectStructure(可以创建很多个不同的ObjectStructure来代表不同评价,然后把同样评价的人放到一个ObjectStructure中)
ObjectStructure* objectStructure = new ObjectStructure;
objectStructure->attach(new Man);
objectStructure->attach(new Woman);
//假如歌手获得成功
Success* success = new Success;
objectStructure->display(success);
cout << "=======================" << endl;
//假如歌手失败了
Fail* fail = new Fail;
objectStructure->display(fail);
return 0;
}
总结:
- 假设我们要添加一个Wait的状态类,考察Man类和Woman类的反应,由于使用了双分派,只需增加一个Action子类即可在客户端调用即可,不需要改动任何其他类的代码
优点:
- 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
- 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点:
- 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
- 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
- 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.
Chain of Responsibility 模式(责任链模式)
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
**主要解决:**职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
**何时使用:**在处理消息的时候以过滤很多道。
举例:
编写程序完成学校OA系统的采购审批项目:需求:
- 采购员采购教学器材
- 如果金额 小于等于5000, 由教学主任审批
- 如果金额 小于等于10000, 由院长审批
- 如果金额 小于等于30000, 由副校长审批
- 如果金额 超过30000以上,有校长审批
代码:
#include <iostream>
using namespace std;
//请求类
class PurchaseRequest
{
public:
int getType() const
{
return type;
}
float getPrice() const
{
return price;
}
int getId() const
{
return id;
}
PurchaseRequest(const int type, const float price, const int id)
: type(type),
price(price),
id(id)
{
}
private:
//请求类型
int type;
//价格
float price = 0.f;
int id = 0;
};
class Approver
{
public:
void setApprover(Approver* const approver)
{
this->approver = approver;
}
explicit Approver(const string& name)
: name(name)
{
}
//处理审批请求的方法,得到一个请求,处理是子类完成的,因此该方法写成纯虚方法
virtual void processRequest(PurchaseRequest* purchaseRequest)=0;
protected:
//下一个处理者
Approver* approver;
//名字
string name;
};
//教学主任
class DepartmentApprover:public Approver
{
public:
explicit DepartmentApprover(const string& name)
: Approver(name)
{
}
void processRequest(PurchaseRequest* purchaseRequest) override
{
if(purchaseRequest->getPrice()<=5000)
{
cout << "请求编号id=" << purchaseRequest->getId() << "被" << this->name << "处理" << endl;
}
else
{
approver->processRequest(purchaseRequest);
}
}
};
//院长
class CollegeApprover :public Approver
{
public:
explicit CollegeApprover(const string& name)
: Approver(name)
{
}
void processRequest(PurchaseRequest* purchaseRequest) override
{
if (purchaseRequest->getPrice() > 5000 && purchaseRequest->getPrice() <= 10000)
{
cout << "请求编号id=" << purchaseRequest->getId() << "被" << this->name << "处理" << endl;
}
else
{
approver->processRequest(purchaseRequest);
}
}
};
//副校长
class ViceSchoolMasterApprover :public Approver
{
public:
explicit ViceSchoolMasterApprover(const string& name)
: Approver(name)
{
}
void processRequest(PurchaseRequest* purchaseRequest) override
{
if (purchaseRequest->getPrice() > 10000 && purchaseRequest->getPrice() <= 30000)
{
cout << "请求编号id=" << purchaseRequest->getId() << "被" << this->name << "处理" << endl;
}
else
{
approver->processRequest(purchaseRequest);
}
}
};
//校长
class SchoolMasterApprover :public Approver
{
public:
explicit SchoolMasterApprover(const string& name)
: Approver(name)
{
}
void processRequest(PurchaseRequest* purchaseRequest) override
{
if (purchaseRequest->getPrice() > 30000)
{
cout << "请求编号id=" << purchaseRequest->getId() << "被" << this->name << "处理" << endl;
}
else
{
approver->processRequest(purchaseRequest);
}
}
};
int main(int argc, char* argv[])
{
//创建一个请求
PurchaseRequest* purchaseRequest = new PurchaseRequest(1, 1000, 1);
//创建相关的审批人
DepartmentApprover* department = new DepartmentApprover("张主任");
CollegeApprover* college = new CollegeApprover("李院长");
ViceSchoolMasterApprover* viceSchoolMaster = new ViceSchoolMasterApprover("王副校长");
SchoolMasterApprover* schoolMaster = new SchoolMasterApprover("佟校长");
//需要将各个审批级别的下一个人设置好(处理人构成一个环装就可以从任何一个人开始处理了)
//否则必须从第一个处理人开始
department->setApprover(college);
college->setApprover(viceSchoolMaster);
viceSchoolMaster->setApprover(schoolMaster);
schoolMaster->setApprover(department);
//开始处理请求
viceSchoolMaster->processRequest(purchaseRequest);
return 0;
}
总结:
- 将请求和处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
- 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web中Tomcat对Encoding的处理、拦截器
Iterator 模式(迭代器模式)
迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。而且不管这些对象是什么都需要遍历的时候,就应该选择使用迭代器模式
举例:
代码:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//迭代抽象类,用于定义得到开始对象、得到下一个对象、判断是否到结尾、当前对象等抽象方法,统一接口
class Iterator
{
public:
Iterator() {};
virtual ~Iterator() {};
virtual string First() = 0;
virtual string Next() = 0;
virtual string CurrentItem() = 0;
virtual bool IsDone() = 0;
};
//聚集抽象类
class Aggregate
{
public:
virtual int Count() = 0;
virtual void Push(const string& strValue) = 0;
virtual string Pop(const int nIndex) = 0;
virtual Iterator* CreateIterator() = 0;
};
//具体迭代器类,继承Iterator 实现开始、下一个、是否结尾、当前对象等方法
class ConcreteIterator : public Iterator
{
public:
ConcreteIterator(Aggregate* pAggregate) :m_nCurrent(0), Iterator()
{
m_Aggregate = pAggregate;
}
string First()
{
return m_Aggregate->Pop(0);
}
string Next()
{
string strRet;
m_nCurrent++;
if (m_nCurrent < m_Aggregate->Count())
{
strRet = m_Aggregate->Pop(m_nCurrent);
}
return strRet;
}
string CurrentItem()
{
return m_Aggregate->Pop(m_nCurrent);
}
bool IsDone()
{
return ((m_nCurrent >= m_Aggregate->Count()) ? true : false);
}
private:
Aggregate* m_Aggregate;
int m_nCurrent;
};
//具体聚集类 继承
class ConcreteAggregate : public Aggregate
{
public:
ConcreteAggregate() :m_pIterator(NULL)
{
m_vecItems.clear();
}
~ConcreteAggregate()
{
if (NULL != m_pIterator)
{
delete m_pIterator;
m_pIterator = NULL;
}
}
Iterator* CreateIterator()
{
if (NULL == m_pIterator)
{
m_pIterator = new ConcreteIterator(this);
}
return m_pIterator;
}
int Count()
{
return m_vecItems.size();
}
void Push(const string& strValue)
{
m_vecItems.push_back(strValue);
}
string Pop(const int nIndex)
{
string strRet;
if (nIndex < Count())
{
strRet = m_vecItems[nIndex];
}
return strRet;
}
private:
vector<string> m_vecItems;
Iterator* m_pIterator;
};
int main()
{
ConcreteAggregate* pName = NULL;
pName = new ConcreteAggregate();
if (NULL != pName)
{
pName->Push("hello");
pName->Push("word");
pName->Push("cxue");
}
Iterator* iter = NULL;
iter = pName->CreateIterator();
if (NULL != iter)
{
string strItem = iter->First();
while (!iter->IsDone())
{
cout << iter->CurrentItem() << " is ok" << endl;
iter->Next();
}
}
system("pause");
return 0;
}
总结:
- 提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
- 隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
- 提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
- 当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式
Interpreter 模式(解释器模式)
-
在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
-
解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
-
应用场景:
1、应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
2、一些重复出现的问题可以用一种简单的语言来表达
3、一个简单语法需要解释的场景
4、这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等
代码:
#include <iostream>
#include <string>
using namespace std;
class Context
{
public:
Context(int num)
{
m_num = num;
}
public:
void setNum(int num)
{
m_num = num;
}
int getNum()
{
return m_num;
}
void setRes(int res)
{
m_res = res;
}
int getRes()
{
return m_res;
}
private:
int m_num;
int m_res;
};
class Expression
{
public:
virtual void interpreter(Context *context) = 0;
};
class PlusExpression : public Expression
{
public:
virtual void interpreter(Context *context)
{
int num = context->getNum();
num++;
context->setNum(num);
context->setRes(num);
}
};
class MinusExpression : public Expression
{
public:
virtual void interpreter(Context *context)
{
int num = context->getNum();
num--;
context->setNum(num);
context->setRes(num);
}
};
int main(int argc, char **argv)
{
Context *pcxt = new Context(10);
Expression *e1 = new PlusExpression();
e1->interpreter(pcxt);
cout << "PlusExpression: " << pcxt->getRes() << endl;
Expression *e2 = new MinusExpression();
e2->interpreter(pcxt);
cout << "MinusExpression: " << pcxt->getRes() << endl;
delete e1;
delete e2;
delete pcxt;
return 0;
}
优点:
- 可扩展性比较好,灵活。
- 增加了新的解释表达式的方式。
- 易于实现简单文法。
缺点:文章来源:https://www.toymoban.com/news/detail-832746.html
- 可利用场景比较少。
- 对于复杂的文法比较难维护。
- 解释器模式会引起类膨胀。
- 解释器模式采用递归调用方法。
引用
1)设计模式精解-GoF 23 种设计模式解析附
2)菜鸟教程——设计模式
3)尚硅谷——设计模式
4)黑马——C++设计模式文章来源地址https://www.toymoban.com/news/detail-832746.html
到了这里,关于C++设计模式(全23种)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!