1 创建型模式
1.1 工厂模式
解决的问题:
(1)我们经常会抽象出一些类的公共接口以形成抽象基类或者接口。这样我们可以通过声明一个指向基类的指针来指向实际的子类实现,达到了多态的目的。所以就不得不在要用到子类的地方写new 对象。这样实体类的使用者必须知道实际的子类名称,以及会使程序的扩展性和维护变得越来越困难。
(2)在父类中并不知道具体要实例化哪一个具体的子类。只能在父类中写方法调用,具体调用哪一个类的方法交给子类实现。
以上两个问题也就引出了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 了
其实工厂模式就是将对象的创建过程封装到一个类中,通过该类来统一创建对象。这样做的好处是能够降低代码的耦合度,提高代码的可维护性和可扩展性。
1.2 抽象工厂模式
//抽象工厂模式
#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 模式实现。
抽象工厂模式相对于简单工厂模式的优势在于,它可以为每个产品族提供一个工厂,并且可以轻松地增加新的产品族而无需修改现有代码,从而符合开闭原则。
1.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)模式在一起使用,因为系统中工厂对象一般来说只要一个
1.4 建造者模式
个人觉得大学生活就是一个Builder模式的最好体验:要完成大学教育,一般将大学教育过程分成4个学期进行,因此没有学习可以看作是构建完整大学教育的一个部分构建过程,每个人经过这4年的(4个阶段)构建过程得到的最后的结果不一样,因为可能在四个阶段的构建中引入了很多的参数(每个人的机会和际遇不完全相同)。
建造者模式的目的是将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。通常当一个对象的构建过程比较复杂、需要多个步骤,并且需要根据不同的配置参数生成不同的表示时,可以考虑使用建造者模式。
下面是建造者模式的一些核心要素:
- 产品(Product):待构建的复杂对象,它由多个部分组成。
- 抽象建造者(Builder):定义构建产品各个部分的接口,以及返回最终产品的方法。
- 具体建造者(Concrete Builder):实现抽象建造者接口,负责具体产品部件的构造和装配。
- 指挥者(Director):调用建造者接口,按照特定顺序组织建造过程,最后返回构建好的产品。
Builder模式的关键是其中的Director对象并不直接返回对象,而是通过一步步(BuildPartA,BuildPartB,BuildPartC)来一步步进行对象的创建。当然这里Director可以提供一个默认的返回对象的接口(即返回通用的复杂对象的创建,即不指定或者特定唯一指定BuildPart中的参数)。
例子:
//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 模式强调的是为创建多个相互依赖的对象提供一个同一的接口。
1.5 原型模式
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,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制
2 结构型模式
2.1 桥接模式
总结面向对象实际上就两句话:一是松耦合(Coupling),二是高内聚(Cohesion)。面向对象系统追求的目标就是尽可能地提高系统模块内部的内聚(Cohesion)、尽可能降低模块间的耦合(Coupling)
桥接模式(Bridge Pattern)是一种结构型设计模式,它旨在将抽象部分与其实现部分分离,使它们能够独立地变化。这种模式是针对继承关系的替代方案,提供更加灵活的设计方式。
在桥接模式中,有两个独立的层次结构:抽象部分和实现部分。抽象部分负责定义高层接口,而实现部分则提供具体的实现。通过桥接模式,两个层次结构可以独立地扩展,而不会相互影响。
//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;
}
总结:
- 桥接模式实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。
- 对于两个独立变化的维度,使用桥接模式再适合不过了。
- 分离抽象接口及其实现部分。提高了比继承更好的解决方案
实际上上面使用Bridge模式和使用带来问题方式的解决方案的根本区别在于是通过继承还是通过组合的方式去实现一个功能需求。因此面向对象分析和设计中有一个原则就是:Favor Composition Over Inheritance。
2.2 适配器模式
适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。它解决了两个不兼容接口之间的互操作问题。
适配器模式主要包含以下几个角色:
- 目标接口(Target Interface):定义客户端所期望的接口。
- 适配器(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;
}
Adapter模式实现上比较简单,要说明的是在类模式Adapter中,我们通过private继承Adaptee获得实现继承的效果,而通过public继承Target获得接口继承的效果。
2.3 装饰器模式
装饰器提供了一种给类增加职责的方法,不是通过继承实现的,而是通过组合,也就是说装饰器模式(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 模式来解决。(装饰器实质上是获取到要装饰的函数或类的引用,然后对其进行一些额外的操作。)
2.4 组合实体模式
合实体模式是一种结构型设计模式,用于将对象组合成树形结构以表示"部分-整体"的层次关系。这种模式使得用户对单个对象和组合对象的使用具有一致性。
这个模式的特点包括:
- 抽象构件(Component):定义了叶子节点和组合节点的共同接口,可以在其中包含一些默认的实现。
- 叶子构件(Leaf):表示树中的叶子节点,没有子节点,继承自抽象构件。
- 组合构件(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;
总结:Component类定义了操作接口,Leaf类和Composite类分别表示叶子节点和组合节点,实现了对应的操作。Composite类可以管理多个Component(包括Leaf和其他Composite),并且在Operation方法中递归调用其子节点的操作,实现了统一的操作接口。
2.5 享元模式
如果一个应用程序使用了太多的对象,就会造成很大的存储开销。特别是对于大量轻量级(细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为没有字母创建一个对象的话,系统可能会因为大量的对象而造成存储开销的浪费。
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;
}
总结:享元方式可以确保相同内部状态的对象只会被创建一次并进行共享,从而节省了内存空间。通过共享对象,可以减少对象的数量和创建的开销,提高程序的性能。享元模式的核心思想是共享对象,通过共享提高性能和节省资源。
2.6 外观模式
外观模式为现有的复杂系统提供一个简单的接口,以便于客户端使用。这个接口隐藏了系统的复杂性,并且让客户端能够更容易地使用系统。
下面举一个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();
}
2.7 代理模式
代理模式允许我们创建一个替代或代理对象,以控制对真实对象的访问。代理对象充当了客户端与真实对象之间的中介,通过代理对象可以间接地访问真实对象,从而在不改变原始代码的情况下增加额外的功能。
代理模式的关键角色包括以下几个部分:
-
抽象主题(Subject):定义了真实主题和代理主题的共同接口,客户端通过抽象主题访问真实主题。
-
真实主题(Real Subject,concreteSubject):定义了具体的业务逻辑,是代理对象所代表的真实对象。
-
代理主题(Proxy Subject):实现了抽象主题接口,持有对真实主题的引用。代理对象可以在不影响真实主题的情况下,控制对真实主题的访问并增加额外的功能。
现在有一个操作系统,只需要调用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();
}
总结: 代理模式和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
3 行为模式
3.1 模板模式
面向对象系统的分析与设计过程中经常会遇到这样一种情况:对于某一个业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用算法)是相同的。Template 提供了这种情况的一个实现框架。Template 模式是采用继承的方式实现这一点:将逻辑(算法)框架放在抽象基类中,并定义好细节的接口,子类中实现细节。
emplate模式实际上就是利用面向对象中多态的概念实现算法实现细节和高层接口的松耦合。可以看到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函数中的煮水操作其实可以省去 直接冲泡即可,但是我们就无法再次使用这个模板了,这样就导致了不能复用子类的实现步骤。
3.2 策略模式
Strategy模式和Template模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。Strategy模式将逻辑(算法)封装到一个类(Context)里面,通过组合的方式将具体算法的实现在组合对象中实现,再通过委托的方式将抽象接口的实现委托给组合对象实现。
举例:
假如现在有个英雄需要使用武器对付敌人,武器有两种匕首和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 的形式调用武器使用的,这就比继承要好得多,因为在面向对象的设计中的有一条很重要的原则就是:优先使用对象组合,而非类继承。
优点:算法可以自由切换。避免使用多重条件判断。扩展性良好。
缺点:策略类会增多。所有策略类都需要对外暴露
3.3 State模式
每个人、事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State)。
主要解决:
当状态数目不是很多的时候,Switch/Case 可能可以搞定。但是当状态数目很多的时候(实际系统中也正是如此),维护一大组的Switch/Case 语句将是一件异常困难并且容易出错的事情。
状态逻辑和动作实现没有分离。在很多的系统实现中,动作的实现代码直接写在状态的逻辑当中。这带来的后果就是系统的扩展性和维护得不到保证。
在State模式中我们将状态逻辑和动作实现进行分离。当一个操作中要维护大量的case分支语句,并且这些分支依赖于对象的状态。State模式将每一个分支都封装到独立的类中。State模式典型的结构图为:
3.4 观察者模式
Observer 模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变。最常见的一个例子就是:对同一组数据进行统计分析时候,我们希望能够提供多种形式的表示(例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
这里的目标Subject提供依赖于它的观察者Observer的注册(Attach)和注销(Detach)操作,并且提供了使得依赖于它的所有观察者同步的操作(Notify)。观察者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;
}
3.5 备忘录模式
没有人想犯错误,但是没有人能够不犯错误。犯了错误一般只能改过,却很难改正(恢复)。世界上没有后悔药,但是我们在进行软件系统的设计时候是要给用户后悔的权利(实际上可能也是用户要求的权利:)),我们对一些关键性的操作肯定需要提供诸如撤销(Undo)的操作。那这个后悔药就是 Memento 模式提供的。
Memento模式的关键就是要在不破坏封装行的前提下,捕获并保存一个类的内部状态,这样就可以利用该保存的状态实施恢复操作。
- Originator 原始的状态
- Memento 包含了要被恢复的对象的状态
//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;
}
总结:
优点:
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
3.6 中介者模式
在面向对象系统的设计和开发过程中,对象之间的交互和通信是最为常见的情况,因为对象间的交互本身就是一种通信。在系统比较小的时候,可能对象间的通信不是很多、对象也比较少,我们可以直接硬编码到各个对象的方法中。但是当系统规模变大,对象的量变引起系统复杂度的急剧增加,对象间的通信也变得越来越复杂,这时候我们就要提供一个专门处理对象间交互和通信的类,这个中介者就是 Mediator 模式。所以Mediator 模式的实现关键就是将对象 Colleague 之间的通信封装到一个类种单独处理。
Mediator模式中,每个Colleague维护一个Mediator,当要进行交互,ConcreteColleagueA和ConcreteColleagueB之间的交互就可以通过ConcreteMediator提供的DoActionFromAtoB来处理,ConcreteColleagueA和ConcreteColleagueB不必维护对各自的引用,甚至它们也不知道各个的存在。Mediator通过这种方式将多对多的通信简化为了一(Mediator)对多(Colleague)的通信。
//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;
}
总结:
- 何时使用:多个类相互耦合,形成了网状结构,需要将上述网状结构分离为星型结构的时候。
- 中介者承担了较多的责任,一旦中介者出现问题,整个系统都会收到影响
3.7 命令模式
- 请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
- Command 模式的最终目的就是将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化
//命令模式
#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;
}
3.8 访问者模式
在面向对象系统的开发和设计过程,经常会遇到一种情况就是需求变更(Requirement Changing),经常我们做好的一个设计、实现了一个系统原型,咱们的客户又会有了新的需求。我们又因此不得不去修改已有的设计,最常见就是解决方案就是给已经设计、实现好的类添加新的方法去实现客户新的需求,这样就陷入了设计变更的梦魇:不停地打补丁,其带来的后果就是设计根本就不可能封闭、编译永远都是整个系统代码。
Visitor模式则提供了一种解决方案:将更新(变更)封装到一个类中(访问操作),并由待更改类提供一个接收接口,则可达到效果。Visitor模式的关键是双分派(Double-Dispatch)的技术。
在 Visitor 模式中,有两个核心角色:
-
Visitor(访问者):定义了对元素对象的新操作接口,通常会定义多个访问者以支持不同的操作。
-
Element(元素):代表被访问的对象,它接受访问者对象的访问。
#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子类即可在客户端调用即可,不需要改动任何其他类的代码
3.9 责任链模式
责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
**主要解决:**职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
举例:
- 采购员采购教学器材
- 如果金额 小于等于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;
}
总结:Chain of Responsibility模式的最大的一个有点就是给系统降低了耦合性,请求的发送者完全不必知道该请求会被哪个应答对象处理,极大地降低了系统的耦合性。
3.10 迭代器模式
迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。而且不管这些对象是什么都需要遍历的时候,就应该选择使用迭代器模式
#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;
}
3.11 解释器模式
一些应用提供了内建(Build-In)的脚本或者宏语言来让用户可以定义他们能够在系统中进行的操作。Interpreter模式的目的就是使用一个解释器为用户提供一个一门定义语言的语法表示的解释器,然后通过这个解释器来解释语言中的句子。
#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;
}
参考:
1)设计模式精解-GoF 23 种设计模式解析附