项目地址:unity的设计模式学习项目
从github上面扒拉的一个设计模式的项目,里面包含了23种GOF设计模式和针对游戏开发的另一些设计模式
每一个设计模式都对应几个小demon,Structure是这个模式的大致框架,还有样例可以提供参考理解
这篇博客是我自己记录的这几天的23种常用设计模式的学习,也是我对他们的理解
行为型模式
这些设计模式主要用来设计多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务
包括以下11个
1命令模式:
命令模式将“请求”封装成对象,两者之间通过命令对象进行沟通
- Command:定义命令的接口,声明执行的方法。
- ConcreteCommand:具体命令,通常包含一个命令的参数和Receive,由receive来完成命令的操作
- Receive:真正执行命令的对象,要能实现命令所要求的功能
- Invoke:通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者
这个小demon非常多,足够你来理解了
2责任链模式:
使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止
就是把所有的处理请求的类练成一条链子,把请求放到链的开始,然后链上的每一个类都去判断一下是不是自己所处理的请求,如果不是就交给下一个来处理
3解释器模式:
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
没学,书上就一句话概括了,啥都没讲,估计不重要
4迭代器模式:
提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节
你自己类比一下c++中的迭代器,和那玩意应该差不多
感觉这个没什么说的,看一看小demon差不多理解差不多了
5中介模式:
用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互
没有中介模式:
使用中介模式:
中介类可以理解为快递的集货中心,所有的快递先送到这里,然后在根据地址送到目的地
6备忘录模式:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
unity可以用playerPrefs或者文件存储都行
7观察者模式:
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
举个例子:例如100万个粉丝关注了肖战,那么当肖战的微博更新的时候,就会向着100万个粉丝发送消息动态已经更新
- Subject:也叫抽象目标类,一个用于保存观察者对象的聚集类并且含有增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- Concrete Subject:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- Observer:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- Concrete Observer:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
8状态模式:
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。
个人理解:假设人物动画有Idle Move Shoot Die四个状态,可以把这四个状态做出四个类,然后具体每一个状态的行为写在类里面,这样当切换状态的时候,切换到另一个类就行了
- Context:定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
- State:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
- Concrete State:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
9策略模式:
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换、
感觉和这个和状态模式怎么这么像呢
- context:持有一个策略类的引用,可以切换具体的策略,也可以调用策略的具体算法,这个类是方便给外部调用
- Strategy:策略类接口
- Concrete Strategy:实现了抽象策略定义的接口,提供具体的算法实现。
10模板方法模式:
定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
就是父类的方法是虚的或者抽象的,子类可以重写他的方法,
我个人的理解就是多态的实现
11访问者模式:
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
假设游戏中有n个玩家,类1需要这些玩家的所有血量值,类2需要这些玩家的所有攻击值
就可以把 类1 和 类2 做成访问者,每一个访问者只需要实现特定的功能
但是访问者模式的缺陷也很明显,就是当Element新增加子类的时候,访问者也需要增加对应的方法,同时也引起所有的子类也要进行相同的改动
结构大概就是这样子
ObjectStructure是被访问者的集合 Visitor是访问者,Element是被访问者基类
创建型模式
主要关注怎么生产一个对象包括以下6个设计模式
1简单工厂模式 工厂方法模式 抽象工厂模式
简单工厂模式:
就是一堆产品,只有一个工厂类,当增产品的时候,只要在工厂里新增方法即可:
工厂方法模式:
每一个产品对应每一个工厂
抽象工厂模式:
工厂方法模式这生产同一类型的产品,抽象工厂模式一个工厂可以生产多种类型产品,同一类型的不同产品分别有不同工厂生产
反正我目前是理解不来,没有做过这东西,不知道具体该用在哪儿
2建造者模式:
将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成
和工厂模式也有点类似,但是当一个对象的构造比较复杂的时候采用这个,工厂模式适合对象的构造不复杂的情况
- Product:它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
- Builder:它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回产品的方法 getResult()。
- Concrete Builder:实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
- Director:调用哪一个具体建造者
3原型模式:
就是拷贝一个对象分为 浅和深 拷贝,浅拷贝就是含有指针的东西还是指向原来的内存块,就在只拷贝了值,引用还是没变
Unity的GameObject.Instantiate()就是一个原型模式
当然c#也有另外的写法 MemberwiseClone()函数。可以克隆一个对象,但是只是浅拷贝
public override object Clone()
{
return this.MemberwiseClone();
}
4单列模式:
类不能实例化,保证只有一个单列,方便其他的类调用,但凡做过一点点项目都应该知道这个东西,太常用了
单列模式的写法:
写法1:
sealed class Singleton
{
private Singleton() { }
private static Singleton _instance;
public static Singleton Instance
{
get
{
if (_instance == null)
_instance = new Singleton();
return _instance;
}
}
}
常规写法就是这个样子,但是当遇到多线程就会有麻烦,在两个if同时判断的时候有可能同时构造对象,这样就构建了两个对象,不符合规定了就
写法2:
加锁,每一个线程进入的时候都要上锁,可避免上述情况
sealed class Singleton
{
private Singleton() { }
private static Singleton _instance = null;
private static readonly object syncObj = new object();
public static Singleton Instance
{
get
{
lock (syncObj)
{
if (_instance == null)
_instance = new Singleton();
}
return _instance;
}
}
}
但是这样还是不够好,因为每一次进入都要加锁,比较耗时,能不能省去这个东西呢
写法3:
在instance为空的时候需要加锁,不为空就不需要加锁
sealed class Singleton
{
private Singleton() { }
private static Singleton _instance = null;
private static readonly object syncObj = new object();
public static Singleton Instance
{
get
{
if (_instance == null)
{
lock (syncObj)
{
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
}
但是这样代码比较复杂容易出错,我们可以用c#的静态构造函数来解决这个问题
写法4:
sealed class Singleton
{
private Singleton() { }
private static Singleton _instance = new Singleton();//在静态构造中完成
public static Singleton Instance
{
get
{
return _instance;
}
}
}
但是还是有缺陷,因为我们静态在编译阶段就已经执行了,而我们的需求是要在使用的时候才实例化这个对象,这样就降低了内存的利用率
写法5:
在单列的类中构造一个内部类,这样只有当调用的时候才能触发其静态构造函数
sealed class Singleton
{
private Singleton() { }
public static Singleton Instance
{
get
{
return Nested._instance;
}
}
class Nested
{
internal static readonly Singleton _instance = new Singleton();
}
}
结构型模式
将类或对象按某种布局组成更大的结构
1适配器模式:
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
类适配器:
Adapter分别继承Target和Adaptee两个类,然后我的理解就是作为一个调用的中介者
对象适配器:
Adapter实现Target接口并使用了Adaptee类,实现接口里面的函数,在函数内部调用Adaptee里面对应的函数就完成了转换
2桥接模式:
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
个人的理解就是: 解决两个群组之间出现的交叉组合情况,典型的面向接口编程
举个列子帮助你理解一下:
在cf里面,角色可以使用武器,
如果这样的话,当需求增加一个角色葵和一个武器的时候
那么可想而知,采用这样的方式写代码,需要改动大量的代码
而采用桥接模式就是这个样子:
这样玩家只要持有武器的接口,不需要知道使用的是那种武器了,武器的具体攻击逻辑只要在子类实现,角色不需要知道
3组合模式:
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
也就是组合成这个样子:
大致类图就是这样:
可以打开项目的结构看一下:
以下是整条树的递归遍历:
4装饰模式:
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式
在继承的基础上添加新功能之外,使用装饰模式也可以,而且项目后期维护听说基本上都是靠这样添加功能的
先看以下大致框图
在具体的装饰类里面,调用原来的具体构件,然后在调用添加的功能,这样就完好的实现了扩展的功能
具体的小demon可以打开这个看一看,可以很好的帮助你来理解:
5外观模式:
要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。
我的理解就是
1 对内部的进行再次封装一层,这样的话内部的代码改变就不会引起外部的改变,只需要改变这个外观类就行了,比较的方便该需求,而且也不需要知道内部的逻辑
2 外部需要依赖很多内部,这样耦合度就很高,用一个中间层外观类可以很好解耦
6享元模式:
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
意思就是相同的对象只要保存一个,下一次在用的时候就不需要重新制造一个了,所谓共享
当然有些部分是可以共享的这就是内部状态,不能共享的叫做外部状态
- FlyWeightFactory:
- 负责产生和管理FlyWeight组件
- 内部通常使用容器来存储
- 当产生的是共享组件就放到容容器里面
- Flyweight: 操作接口
- ConcreteFlyweight: 共享组件
- UnConcreFlyweight: 不可共享组件
- 定义为单独的组件,不包含任何共享资源
7代理模式:
为其他对象提供一种代理以控制对这个对象的访问。
RealSubject类和他的代理类同时实现Subject接口,这样客户只需要调用代理类就行
就相当于给老板提供了一个秘书,什么事情传达给秘书来就行,秘书在传达给老板,当然秘书可以进行判断是不是需要告诉老板这个消息