一、OOP三大特征和五大设计原则

1、封装

封装就是把数据和对数据进行的操作集合在一起,然后将实现与使用分离,只暴露部分接口和属性供外部使用。

2、继承

继承,可以让某个类型的对象获得另一个类型的对象的属性和方法,并在无需重新编写原来的类的情况下对这些功能进行扩展。继承增加了类的可重用性。

3、多态

多态指同一个行为具有多个不同表现形式或形态,一般通过子类继承父类实现,同一个方法在多个子类中有着不同的实现,这样在实例化这些子类的对象调用相同的接口后却可以获得完全不同的结果。

1、依赖倒置原则

上层模块不应该依赖于下层模块,他们应该共同依赖于一个抽象。细节依赖于抽象,抽象不依赖于细节。常用实现方式:在代码中使用抽象类,而将具体类放在配置文件中。

2、开放-封闭原则

一个软件或软件模块应该对功能的扩展是开放的,是支持的,但是对于已经写好的部分的修改应该是封闭的,是尽可能少的。比如一个软件,原本只能解析xml格式文件,现在需要添加解析json格式文件的功能,这时不应该对原有的解析xml文件模块进行大规模修改,而可以添加解析json格式文件的功能,这就需要在设计之初将解析流程中公共的部分抽象出来,对抽象编程而不是对具体编程。

3、单一职责原则

单一职责原则有两层含义,一是一个类不应该承担太多职责,二是相同的职责不应该分给多个不同的类,总的来说就是需要职责明细。

4、接口隔离原则

客户端不应该依赖于那些它不需要的接口,即一个类的肥胖接口需要用多个接口替代之,这样客户端对该类的依赖性就建立在了最少的接口上。

5、里氏替换原则

子类应该可以在任何场景下替换父类。一般就是在子类中实现或者重写所有父类的方法。

二、结构型模式

1、代理模式※

(1)代理模式的定义:

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。
Proxy模式在访问对象时引入了一定程度的间接性。根据代理的类型,附加的间接性有很多种用途:
远程代理(Remote Proxy)可以隐藏一个对象存在于不同地址空间的事实。
虚代理(Virtual Proxy)可以进行最优化,例如根据要求创建对象。
保护代理(Protection Proxies)和智能引用(Smart Reference)都允许在访问一个对象时有一些附加的内务处理。
图片10.png

(2)为什么要用代理模式?

中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

(3)优缺点:

A.优点
代理模式能够协调调用者和被调用者吗,在一定程度上降低了系统的耦合度。
远程代理是的客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
保护代理可以控制对真实对象的使用权限。
B. 缺点
由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
实现代理模式需要额外的工作,有些代理秘书的实现比较复杂。

(4) 适用性

下面是一些可以使用Proxy模式常见的情况:
远程代理(Remote Proxy) 为一个对象在不同的地址空间提供局部代表。
虚代理(Virual Proxy) 根据需要创建开销很大的对象。
保护代理(Protection Proxy) 控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
智能指引(Smart Reference) 取代了简单的指针,它在访问对象时执行一些附加操作。典型用途包括:
对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它。
当第一次引用一个持久对象时,将它装入内存。
在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

(5)与适配器模式的区别

适配器模式是由于原接口与目标接口不兼容而产生的。代理模式则拥有与被代理者相同的接口,但是其具有自己独有的职责,将使用与被代理者的实现隔离开来。

(6)动态代理

Java使用JDK实现动态代理类技术核心为Proxy类和一个InvocationHandler 接口。

参考:https://www.cnblogs.com/daniels/p/8242592.html
https://blog.csdn.net/huangjh2017/article/details/78595072

2、装饰模式※

(1)概念

时常会遇到这样一种情况,我已经设计好了一个接口,并且也有几个实现类,但是这时我发现我设计的时候疏忽了,忘记了一些功能,或者后来需求变动要求加入一些功能,最简单的做法就是修改接口,添加函数,然后继承类中都相应的添加实现,这样做倒也没什么问题,但是如果这种变化来个好几次或者继承类非常多,那工作量可就大了。
所以大神们相出了装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
图片11.png
用法:

int main()  
{    
	Component* pComponent = new ConcreteComponent();      
	ConcreteDecorator* pConDecorator = new ConcreteDecorator();      	pConDecorator->setComponent(pComponent);    
 	pConDecorator->operation();    
	pConDecorator->addBehavior();    
 return 0; 
}

(2)优缺点

A.优点
比静态继承更灵活 与对象的静态继承(多重继承)相比,Decorator模式提供了更加灵活的向对象添加职责的方式。可以用添加和分离的方法,用装饰在运行时刻增加和删除职责。相比之下,继承机制要求为每个添加的职责创建一个新的子类。这会产生许多新的类,并且会增加系统的复杂度。此外,为一个特定的Component类提供多个不同的Decorator类,这就使得你可以对一些职责进行混合和匹配。
避免在层次结构高层的类有太多的特征 Decorator模式提供了一种“即用即付”的方法来添加职责。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,你可以定义一个简单的类,并用Decorator类给它逐渐地添加功能。可以从简单的部件组合出复杂的功能。
B.缺点
Decorator与它的Component不一样 Decorator是一个透明的包装。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
有许多小对象 采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象。这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。

(3)适用性

  以下情况使用Decorator模式
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
当不能采用生成子类的方法进行扩充时。一种情况是,可以有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

参考:https://www.cnblogs.com/cxjchen/p/3161686.html
https://blog.csdn.net/huangjh2017/article/details/78524418

3、适配器模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。——《设计模式:可复用面向对象软件的基础》

(1)类适配器模式(class adapter pattern)

图片12.png

(2)对象适配器模式(object adapter pattern)

图片13.png

(3)缺省适配器模式(default adapter pattern),也叫默认适配器模式、接口适配器模式

图片14.png

(4)缺点:

过多地使用适配器,增加系统理解难度。

(5)和代理模式的区别:

适配器模式是由于原接口与目标接口不兼容而产生的。代理模式则拥有与被代理者相同的接口,但是其具有自己独有的职责,将使用与被代理者的实现隔离开来。

参考:https://www.cnblogs.com/mingmingcome/p/9810731.html
https://www.cnblogs.com/alsf/p/8506912.html

4、外观模式

外观模式通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
图片15.png
参考:https://www.jianshu.com/p/6c721472be83

5、桥接模式

(1)模式来源

设想如果要绘制矩形、圆形、椭圆、正方形,我们至少需要4个形状类,但是如果绘制的图形需要具有不同的颜色,如红色、绿色、蓝色等,此时至少有如下两种设计方案:
• 第一种设计方案是为每一种形状都提供一套各种颜色的版本。
图片16.png
• 第二种设计方案是根据实际需要对形状和颜色进行组合。
图片17.png
对于有两个变化维度(即两个变化的原因)的系统,采用方案二来进行设计系统中类的个数更少,且系统扩展更为方便。设计方案二即是桥接模式的应用。

(2)模式结构

图片18.png
实例a:
图片19.png
这就将pen和color分离开来,各自发展。

实例b,跨平台播放器:
图片20.png

参考:https://blog.csdn.net/huangjh2017/article/details/78472975

6、组合模式

(1)定义:

将对象组合成树形结构以表示“部分-整体”的层次结构,使客户端对单个对象和组合对象保持一致的方式处理。所以,合成模式必须在合适的地方提供子对象的管理方法,诸如:add()、remove()、以及getChild()等。透明式的合成模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定接口,如下。
图片21.png

(2)示例:

文件系统就是典型的组合模式,如下图是window系统D盘符下的部分目录组织结构,红橙色的是目录,浅绿色的是文件,这里目录和文件是可以看成是同一种对象对待。
图片22.png

参考:https://blog.csdn.net/u014727260/article/details/82722473

7、享元模式

(1)定义:

运用共享技术有效地支持大量细粒度的对象。
图片23.png

(2)示例:

String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式。
  比如我们每次创建字符串对象时,都需要创建一个新的字符串对象的话,内存开销会很大,所以如果第一次创建了字符串对象“adam“,下次再创建相同的字符串”adam“时,只是把它的引用指向”adam“,这样就实现了”adam“字符串再内存中的共享。
方法就是用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储。

参考:https://www.cnblogs.com/adamjwh/p/9070107.html

三、创建型模式

1、单例模式※

整个程序有且仅有一个实例。该类负责创建自己的对象,同时确保只有一个对象被创建。

//饿汉式单例
class myStaticSingleton
{
public:
	static myStaticSingleton* getInstance(){
		return &_instance;
	}
private:
	myStaticSingleton() {}
	static myStaticSingleton _instance;
};

这里有一个小知识点,c中的static对象在程序启动的时候就初始化分配了内存,而c++中的static对象因为有构造和析构函数,所以是在第一次使用的时候初始化的,并且由系统维护,在程序生命周期结束后反序析构。

//懒汉式单例
class myMutexSingleton
{
public:
	static volatile myMutexSingleton* getInstance()
	{
		if (!_instance)
		{ 
			lock_guard<mutex> tmplock(_myMutex);
			if (!_instance)
			{
				_instance = new myMutexSingleton();
			} 
		}
		return _instance;
	}

protected:
	myMutexSingleton() {};
	~myMutexSingleton() {
		if (_instance)
		{
			lock_guard<mutex> tmplock(_myMutex);
			if (_instance)
			{
				delete _instance;
			}
		}
	};

private:
	static std::mutex _myMutex;
	static volatile myMutexSingleton* _instance;
};

这里的_instance使用了volatile关键字,是因为简单的双检查锁是不安全的,因为new一个实例实际可以分为了三步:分配内存,初始化对象,将_instance指向新分配内存的地址。然而有的编译器会对这个顺序重新排序,将后两步调换了顺序,这导致在_instance指向新地址后,可能另一个线程来判断_instance是不是null,而此时_instance已经不是null了,于是返回了还未初始化的_instance。

2、工厂方法模式※

定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使的工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
  工厂方法模式是简单工厂模式的衍生,但是工厂方法模式和简单工厂模式又有一定的区别:简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对客户端来说,去除了与具体产品的依赖。但这也是问题的所在,当我们需要添加某个对象时,我们必须要修改对应的工厂类。这可不是一个好的方法,这就等于说,我们不但对扩展开放了,对修改也开放了,这就违背了开放-封闭原则。
  工厂方法模式就是为了解决简单工厂模式存在的问题。首先完全遵循开放-封闭原则,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。但是我们仔细观察也会发现,工厂模式实现时,客户端需要决定实例化哪一个工厂来创建对象,选择判断的问题还是存在,也就是说工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。你想要加功能,本来是修改该工厂类的,而现在是修改客户端。
工厂方法模式对简单工厂模式进行了抽象。有一个抽象的Factory类,这个类将不再负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。在这个模式中,工厂类和产品类往往可以依次对应,即一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。

实现:

class Car
{
public:
	Car();
	~Car();

	virtual void Run() = 0;

private:

};

class SlowCar : public Car
{
public:
	void Run()
	{
		cout << "run slowly" << endl;
	}
};

class FastCar : public Car
{
public:
	void Run()
	{
		cout << "run fast" << endl;
	}

private:

};

class CarFactory
{
public:
	CarFactory();
	~CarFactory();

	virtual Car* CreateCar() = 0;

private:
};

class SlowCarFactory : public CarFactory
{
public:
	Car *CreateCar()
	{
		return new SlowCar();
	}

private:

};

class FastCarFactory : public CarFactory
{
public:
	Car *CreateCar()
	{
		return new FastCar();
	}

private:

};

int main()
{
	CarFactory *myFac = new FastCarFactory();
	Car *myCar = myFac->CreateCar();
	myCar->Run();
}

参考:https://blog.csdn.net/huangjh2017/article/details/78234886

3、抽象工厂模式※

抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
使用抽象工厂模式一般要满足以下条件。
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
当增加一个新的产品族时不需要修改原代码,满足开闭原则。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
图片24.png
图片25.png

class Car
{
public:
	Car() {};
	~Car() {};

	virtual void Run() = 0;
};

class SlowCar : public Car
{
public:
	void Run() {
		cout << "run slowly" << endl;
	}

private:
};

class FastCar : public Car
{
public:
	void Run() {
		cout << "run fast" << endl;
	}

private:
};

class Plane
{
public:
	Plane() {};
	~Plane() {};

	virtual void Fly() = 0;
};

class SlowPlane : public Plane
{
public:
	void Fly() {
		cout << "fly slowly" << endl;
	}

private:
};

class FastPlane : public Plane
{
public:
	void Fly() {
		cout << "fly fast" << endl;
	}

private:
};

class TrafficFactory
{
public:
	TrafficFactory() {};
	~TrafficFactory() {};

	virtual Car* CreateCar() = 0;
	virtual Plane* CreatePlane() = 0;
private:

};

class SlowTrafficFactory : public TrafficFactory
{
public:
	Car* CreateCar() {
		return new SlowCar();
	}

	Plane* CreatePlane()
	{
		return new SlowPlane();
	}

private:
};

class FastTrafficFactory : public TrafficFactory
{
public:
	Car* CreateCar() {
		return new FastCar();
	}

	Plane* CreatePlane()
	{
		return new FastPlane();
	}

private:
};

int main()
{
	TrafficFactory* myFac = new SlowTrafficFactory();
	Car* myCar = myFac->CreateCar();
	Plane* myPlane = myFac->CreatePlane();
	myPlane->Fly();
}

应用场景:
抽象工厂模式最早的应用是用于创建属于不同操作系统的视窗构件。如 java 的 AWT 中的 Button 和 Text 等构件在 Windows 和 UNIX 中的本地实现是不同的。
抽象工厂模式通常适用于以下场景:
当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

参考:http://c.biancheng.net/view/1351.html
https://blog.csdn.net/huangjh2017/article/details/78266955

4、建造者模式

在软件的设计中,我们可能经常会遇到需要构建某个复杂的对象(比如在游戏开发中,进行人物角色的构建),建造该对象的“过程”是稳定的(对于一个人设来都有身体,脸,发型,手脚等),而具体建造的“细节”是不同的(每个人设的身体,脸等各有千秋)。但对于用户来讲,我才不管这些,我只想告诉你,我现在需要某个对象(拥有某特征的人物角色),于是你就创建一个给我就行了。
创建者模式又叫建造者模式,是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。
图片26.png
参考:https://blog.csdn.net/huangjh2017/article/details/78383389

5、原型模式

通过复制一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的原型。它提供了一种快速有效的创建对象的方式。当直接创建对象的代价比较大时可以采用这种方式。
图片27.png
这里说到克隆就涉及到两个知识点了:
浅克隆:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原对象。
深拷贝:深拷贝把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。
乍一看,这和c++中的拷贝构造函数很像,但仔细想想,A aa = a时,确实会执行拷贝构造函数,但是,A* aa = a,并不会执行拷贝构造函数,而且指向子类的父类指针无法构造出一个同样的指向子类的父类对象,所以需要原型模式——实现一个clone()接口。
相同点:原型模式和拷贝构造函数都是要产生对象的复制品。
不同点:原型模式实现的是一个clone接口,注意是接口,也就是基于多态的clone虚函数。也就是说原型模式能够通过基类指针来复制派生类对象。拷贝构造函数完不成这样的任务。
 原型模式的核心是克隆,构造函数只是克隆的一个办法而已。

参考:https://blog.csdn.net/huangjh2017/article/details/78398029
https://www.cnblogs.com/jylz/p/10648439.html

四、行为型模式

1、策略模式※

策略模式定义了算法家族,分别封装起来,让它们之间可以互相转换,此模式让算法的变化,不会影响到使用算法的用户。
策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
图片28.png
在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象。这本身并没有解除客户端需要选择判断的压力,所以策略模式配合常常可以简单工厂使用。

参考:https://www.cnblogs.com/jiese/p/3181099.html
https://www.jianshu.com/p/e7e415c1aed6

2、观察者模式※

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。当一个对象发生了变化,关注它的对象就会得到通知;这种交互也称为发布-订阅(publish-subscribe)。目标是通知的发布者,它发出通知时并不需要知道谁是它的观察者。

class Observer
{
public:
	Observer() {};
	~Observer() {};

	virtual void update(int) = 0;
};

class ConCreteObserverA : public Observer
{
public:
	void update(int state)
	{
		cout << "A get state : " << state << endl;
	}

};

class ConCreteObserverB : public Observer
{
public:
	void update(int state)
	{
		cout << "B get state : " << state << endl;
	}

};

class Subject
{
public:
	Subject() {};
	~Subject() {};

	virtual void connect(Observer* ob) = 0;
	virtual void disconnect(Observer* ob) = 0; 

private:
};

class ConcreteSubject : public Subject
{
public:
	void connect(Observer* ob)
	{
		_myObvs.push_back(ob);
	}

	void disconnect(Observer* ob)
	{
		_myObvs.remove(ob);
	} 

	void SetState(int state)
	{
		_state = state;
		notify();
	}

private:

	void notify()
	{
		for (auto it = _myObvs.begin(); it != _myObvs.end(); ++it)
		{
			(*it)->update(_state);
		}
	}

	list<Observer*> _myObvs;
	int _state;
};

int main()
{
	Observer* oba = new ConCreteObserverA();
	Observer* obb = new ConCreteObserverB();
	ConcreteSubject *sub = new ConcreteSubject();
	sub->connect(oba);
	sub->connect(obb);
	sub->SetState(1);
return 0;
}

参考:https://www.cnblogs.com/carsonzhu/p/5770253.html

3、解释器模式

给定一门语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

4、模板方法模式

定义一个操作的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
实现方案:将算法/逻辑框架放在抽象基类中,并定义好实现接口,在子类中实现细节接口。
注:策略模式,解决的是相同的问题,只是其方案是将各个接口封装为类,通过委托/组合方式解决问题

参考:https://www.cnblogs.com/lang5230/p/5320775.html

5、迭代子模式

提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。

6、责任链模式

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象链成一条链,并沿着这条链传递该请求/命令,直到有对象处理它为止。
注:这里的请求、命令正是可以和命令模式进行结合的地方
参考:https://www.cnblogs.com/lang5230/p/5328166.html

7、命令模式

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。在OOP中,一切都是对象,将请求封装成对象,符合OOP的设计思想,当将客户的单个请求封装成对象以后,我们就可以对这个请求存储更多的信息,使请求拥有更多的能力;命令模式同样能够把请求发送者和接收者解耦,使得命令发送者不用去关心请求将以何种方式被处理。
图片29.png
参考:https://www.cnblogs.com/lizhanwu/p/4435359.html

8、备忘录模式

备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态[DP]。举个简单的例子,我们玩游戏时都会保存进度,所保存的进度以文件的形式存在。这样下次就可以继续玩,而不用从头开始。这里的进度其实就是游戏的内部状态,而这里的文件相当于是在游戏之外保存状态。这样,下次就可以从文件中读入保存的进度,从而恢复到原来的状态。这就是备忘录模式。
图片30.png
 Memento类定义了内部的状态,而Caretake类是一个保存进度的管理者,GameRole类是游戏角色类。

参考:https://blog.csdn.net/wuzhekai1985/article/details/6672906

9、状态模式

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
图片31.png
参考:https://www.cnblogs.com/wrbxdj/p/5361004.html

10、访问者模式

访问者模式(Visitor Pattern)表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
图片32.png
参考:https://blog.csdn.net/liang19890820/article/details/79364406
https://www.cnblogs.com/onlycxue/p/3507112.html

11、中介者模式

用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
我们都知道,面向对象设计鼓励将行为分布到各个对象中。但是,这种分布可能会导致对象间有许多连接。在最坏的情况下,每一个对象都知道其他所有对象,就造成了复杂的关联关系。虽然将一个系统分割成许多对象通常可以增强可复用性,但是对象间相互连接的激增又会降低其可复用性。大量的相互连接使得一个对象似乎不太可能在没有其他对象的支持下工作,这样使得系统表现为一个不可分割的整体。而且,对系统的行为进行任何较大的改动都十分困难,因为行为被分布在许多对象中。结果是,你可能不得不定义很多子类以定制系统的行为。
图片33.png
外观模式与中介者模式的不同之处在于它是对一个对象子系统进行抽象,从而提供了一个更为方便的接口;外观模式的协议是单向的,即外观模式向子系统提出请求,但反过来则不行;而对于中介者模式,是进行多个对象之间的协作,通信是多向的。

参考:https://www.cnblogs.com/ring1992/p/9593451.html