本文最初写于2018/9/4
毛星云 RIP
所以我们只能通过复杂的程序来学习设计模式。
你不管看别人的程序也好,自己写程序练习也好,那必须要复杂,复杂到你不用设计模式就做不下去,这才能起到学习设计模式的作用。
维护一个表格
实际使用的时候,是从当前情景匹配适用情形去找到适合的设计模式
【游戏设计模式】之四 《游戏编程模式》全书内容提炼总结 - 知乎
miloyip/graphvizuml: 使用 Graphviz 绘画 UML 图
Active object - Wikipedia
Balking pattern - Wikipedia
Guarded suspension - Wikipedia
Reactor pattern - Wikipedia
Design Patterns in Dynamic Languages // Python
Channel (programming) - Wikipedia // GO
Hygienic macro - Wikipedia // Scheme
工厂模式(factory Method)的本质是什么?为什么引入工厂模式? - 知乎
工厂就是封装复杂创建流程,非必要不要滥用设计模式,短平快实现需求
我选择的三种UML方案:
Graphviz的方案比较轻量,适合程序员,可以用Git进行版本管理,但是有学习门槛
UML | 适用情景 |
---|---|
类图 | 类OOP设计 |
顺序图 | 时序,前后端交互 |
状态图 | 状态机 |
活动图 | 多线程fork/join流程 |
我个人最常用的是顺序图和状态图,比源码和文字描述的表现力更强
经典设计模式主要适用于C++,C#语言的OOP编程范式
目标是得到高扩展性的代码
在应对频繁需求变更和迭代的过程中能够:
参考:
桥接模式将抽象类和抽象类的组件进行分离。从而达到抽象类和抽象类中的组件可以独立的变化。
参考:
适用情形,两头的代码已经都别人写好了,我来对接时,写一个适配器进行转接
适配器模式的作用是将一个类的接口转换成客户所需要的接口。适配器器模式分为类适配器和对象适配器。
采用多继承的方式,将目标接口和要适配的类进行集成。调用的时候,采用多态的机制,调用要适配的类实现即可。
对象适配的核心思想是客户端需要调用某种方法,而Adaptee没有该方法,为了使客户端能够使用Adaptee类,需
要提供一个包装(Wrapper)类Adapter。对象适配器与类适配器不同的地方是,适配器和被适配者不是继承的关
系,而是组合的关系。
参考:
可以将Coroutine机制封装一下,把一个命令包装成一个Coroutine,再把Coroutine串联在一起
命令模式是将一个请求封装为一个对象,从而使你可用不同的请求对造成不同的行为结果。
主要是因为ctor是特殊函数,不支持虚函数
所以包装成工厂函数,就可以使用一般的多态机制,更加灵活
一个工厂生产多种产品,告诉产品的类型,工厂就会生产对应的产品。如果新增产品的种类,那么需要更改工厂的源代码。
class Factory {
public:
Product* createProduct(productType type) {
switch (type) {
case TypeA:
return new ProductA();
case TypeB:
return new ProductB();
case TypeC:
return new ProductC();
default:
return nullptr;
}
}
};
工厂方法模式的核心思想是:建立一个工厂基类。当我们想生产一种产品的时候,我们继承工厂基类,派生出生产
对应产品的派生类。使用派生出的工厂生产对应的产品。
抽象工厂模式允许生产不同种类的产品。从而有应对了工厂模式只能生产单一种类产品的问题。
当需求的特点是流程的步骤类型相同(有一条步骤类型明确的流水线),但是步骤的具体却是不一样的。每种工人,在实现某一步骤是不一样的。指挥者调用建造者,来通过每种步骤的不一样,来表现出不同的产品。
案例:
代理(Proxy)这个词在具体领域,比如网络,可能有非常多的概念
生活中的中介可以看作代理,中介会帮你完成你本身完成不了的工作
你只需要与中介对接,向中介提出请求,中介去执行,并将结果返回给你,你不需要知道中介是怎么做的
编程中,可以看作是Client-Server模型,Proxy Server做了一个抽象隔离,让Client使用简单的接口完成复杂的任务
为其他对象提供一种代理以控制对这个对象的访问。这样实现了接口和实现的分离。
案例:
本质是数据驱动地去构造一个类型
一般的RPG需要制作大量的换皮怪来应对玩家的等级和装备成长
可以通过功能模块的组合来拼装出一个新的怪物类型,不需要编写代码
核心思想是通过使用拷贝构造函数,来对自身进行拷贝,生成一个新的对象,来进行对象的创建。
参考:
装饰模式能够实现动态的为对象添加功能,是从一个对象外部来给对象添加功能。在不必改变原类文件和使用继承
的情况下,动态地扩展一个对象的功能。
参考:
一种fallback处理机制
案例:
案例:
文件和目录都继承自节点
删除文件就是删除节点,删除目录是删除子树
就是包装类
比如游戏引擎对图形接口和物理引擎都会重新包装一遍,因为原始的接口集合太复杂太难用了
案例:对象缓存或者对象池
就是复用内存和对象
享元模式是为了应对大量细粒度对象重复的问题。程序中存在大量细粒度的对象,每次要使用时都必须创建一个新的对象,既影响了运行效率又增加了内存消耗。享元模式和对象池的区别,享元模式的享元池存储的是种类,一个种类只有一个实例,所有对象共享这一个实例。而对象池中可能存再多个实例。
从多个alternative中选择一个
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法的变化不
会影响到使用算法的客户。
C++/C#自带,这已经不是设计模式了
原始的沙箱模式没有实践意义
沙箱这个概念需要了解:
这个沙箱机制ZeloEngine已经实现,Demo全部在Lua脚本沙箱中编写运行,可以动态切换,开发Demo不会导致频繁编译引擎
【ZeloEngine】沙箱机制_游戏编程扯淡精粹-CSDN博客
案例分析:
transform树的脏标记更新ZeloEngine已经实现
需要框架,内容太多,建议单独查
参考:
典型案例是Java Spring框架
我并不了解Spring,这里只作为案例
控制反转+依赖注入,可以理解为一种基于配置和类反射的工厂模式
下面是一个Spring的配置文件,spellChecker被注入textEditor的属性中,运行时会解析xml,去构造对象
伪代码:textureEditor.spellChecker = new me.leehao.SpellChecker(...)
spellChecker是一个接口,具体是什么类由xml配置来选择,这就是依赖注入
这么做的好处是:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="textEditor" class="me.leehao.TextEditor">
<property name="spellChecker" ref="spellChecker"/>
bean>
<bean id="spellChecker" class="me.leehao.SpellChecker">
bean>
beans>
参考:
WIP