面向对象设计五个基本原则:
单一职责原则、开闭原则、接口隔离原则、里氏替换原则和依赖倒置原则
设计原则:
1、找出程序中会变化的方面,然后将其和固定不变的方面相分离
2、针对接口编程,不针对实现编程
3、多用组合,少用继承
4、为了交互对象之间的松耦合设计而努力
5、类应该对扩展开放,对修改关闭
6、依赖倒置原则:要依赖抽象,不要依赖具体类
7、最少知识原则:只和你的密友谈话。
8、好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给) 你。
9、一个类应该只有一个引起变化的原因。
定义设计模式
模式是在某情境(context)下,针对某问题的某种解决方案。
情境就是应用某个模式的情况。着应该是会不断出现的情况。
问题就是你想在某情境下达到的目标,但也可以是某情境下的约束。
解决方案就是你所追求的:一个通用的设计,用来解决约束、达到目标。
设计模式分类
创建型模式
抽象工厂模式(Abstract Factory)
建造者模式(Builder)
工厂方法模式(Factory Method)
原型模式(Prototype)
单例模式(Singleton)
结构型模式
适配器模式(Adapter):包装一个对象,提供不同的接口
桥接模式(Bridge)
组合模式(Composite)
装饰模式(Decorator):包装一个对象,提供额外的行为
外观模式(Facade):包装许多对象,简化它们的接口
享元(蝇量)模式(Flyweight):
代理模式(Proxy):包装一个对象,控制对它的访问
行为型模式
职责链模式(Chain of Responsibility)
命令模式(Command)
解释器模式(Interpreter)
迭代器模式(Iterator)
中介者模式(Mediator)
备忘录模式(Memento)
观察者模式(Observer)
状态模式(State)
策略模式(Strategy)
模板方法模式(Template Method)
访问者模式(Visitor)
设计模式:
策略模式:定义了算法簇,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
此模式使用场景:分为主体和动作,多对多的关系,例如植物大战僵尸里面的僵尸和<行走,攻击,叫声>等动作。将动作定义成算法簇,可以让僵尸在任何情况下,变化它的动作,比如一开始慢慢走,走到一半快速走(持报僵尸)。
观测者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新
典型的广播关系,可以和注册和注销应用,注册才通知,注销则不通知。
装饰者模式:动态地将这人附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案
应用场景:主体和客体是附加关系,而且是无限附加。我可以在一个主体上无限的附加客体的属性,比如命运之手(head of fate)里面的戒指,就适用于装饰者模式。
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
用于易扩展的基础类的编写,而且扩展的方法千奇百怪的情况。比如UI的加载,基础控件是固定的,其核心不会变,但是控件可以千奇百怪,什么样的衍生控件都有,这时候就需要工厂模式,让具体实现在具体控件中去实现。
抽象工厂模式:提供一个借口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
区别:
抽象工厂模式在抽象工厂里面还抽象了不同产品的方法,然后这些抽象的不同产品的方法,再在具体工厂中进行实现
工厂方法模式: 只有一个抽象产品类,具体工厂类只能创建一个具体产品类的实例
抽象工厂模式: 有多个抽象产品类 ,具体工厂类能创建多个具体产品类的实例
简单工厂模式: 一个工厂,用一个标志位去决定常见产品
单例模式:确保一个类只有一个实例,并提供一个全局访问点
通常用于日志、共享内存等。奇妙的是,这个设计模式和观察者模式相性很好,可以通过单例主题和注册/注销的方式,全局控制事件。比如我需要监听键盘or鼠标事件,我可以创建一个单例监听类,然后用他的实例去注册这些事件,可以节省自己写这些事件的时间。比如可以创建一个每帧回调的单例,如果有需要每帧演算的方法,就可以去进行注册。
代码的核心在于:
1、构造器不外放,只有一个全局获取函数
2、if(xxx == null) xxx = xxx(); 确保只有一个全局实例
3、严格的同步控制,异步处理中,上一个线程完成才能进入获取单例的函数
命令模式:将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
该设计的目的就是将调用者和接收者解耦。
在【需要撤销or回滚的操作】情况下,需要考虑使用这个模式,通过栈去存储命令,然后编写undo方法。服务端和客户端帧同步的时候,也是用这个模式的,尤其在运算量大的情况下,需要客户端去运行一部分逻辑的计算(比如fps墙面射击渲染),这个时候就需要服务端发指令,然后在各个客户端根据指令去进行渲染、计算。
适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
适配器模式和装饰器模式的区别:前者是为了需求和实际内容不兼容而包一个类(主要是针对不兼容的接口作兼容),因此一定会修改接口。后者是为了行为或者功能(也可以说是责任)的扩展而包一个类,因此一定不会改变接口(行为需要保持一致)
适配器模式有对象适配器和类适配器
模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
角色的动作、角色受击动作等,可以把角色受击需要表现的动作序列封装成一个模板,根据受到的不同技能的效果去重新实现具体函数。模板方法和策略模式的区别:继承和组合,模板方法子类重写其方法,策略模式不可以,所以策略模式需要应用于数量有限的算法。工厂方法是模板方法的一种特殊版本。
模板方式和工厂模式的核心思想非常类似, 都是把一些操作留给子类去实现。
模板方法中常常会调用工厂方法的, 他们之间存在着的紧密的情侣关系。
工厂方法模式和模板方法模式的区别在于:
模板方法模式的意义在于固定了一个算法的整体结构, 复用了其中通用的步骤, 将需要定制的部分留给了子类实现
工厂方法模式的意义在于解决了父类没有办法预知应该实现什么子类的问题
迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
组合模式:允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
当有数个对象的集合,它们彼此之间有“整体/部分”关系,就需要用到,游戏界面中,例如滚动框,内部的子界面就是另一个界面,就是这种关系。
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
状态机和行为树的编程中,可以考虑使用该模式。
这个模式和策略模式很类似(通过组合,实现在运行时的改变),但是意图不一样,一个是组合动作,一个是组合状态(状态中包含一组通用的动作)。
代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
代理者是先判断是否需要执行被代理者的功能,如果不需要执行被代理者功能,那么就执行代理者自身的功能,如果需要,那就执行被代理者的功能。
比如客户端发送数据给服务端,如果数据量过大,就需要客户端对发送作代理,进行分包发送。
关于这个模式书上主要讲了远程代理相关的内容,在网络通信上确实经常用到。
保护代理:保护原本的对象,对访问者进行权限控制。
动态代理:代码运行时,才会根据传入的参数,实例代理类。
其他:防火墙代理,智能引用代理,缓存代理,同步代理,复杂隐藏代理,写入时复制代理。
复合模式:复合模式结合两个或以上的模式,组成一盒解决方案,解决一再发生的一般性问题。
MVC就是经典的复合模式,结合了观察者模式、策略模式和组合模式。
模型使用观察者模式,实时更新界面数据,保持两者解耦。控制器是视图的策略。视图之中的UI控件则是组合模式(比如滚动框的子界面)
其他书中未详细介绍的设计模式
1、桥接模式(Bridge Pattern)
使用桥接(Bridge Pattern)不只改变你的实现,也改变你的抽象
用途:
1、适合使用在需要跨越多个平台的图形和接口系统上
2、当需要用不同的方式改变接口和实现时,你会发现桥接模式很好用
优缺点:
1、实现和界面解耦
2、抽象和实现可以独立扩展,不会影响到对方
3、抽象类的改变,不会影响到客户
4、缺点是增加的复杂度
2、生成器模式
使用生成器模式(Builder Pattern)封装一个产品的构造过程,并允许按步骤构造。
用途:
1、经常用来创建者结构
优缺点:
1、讲一个复杂对象的常见过程封装起来
2、允许对象通过多个步骤来创建,并且可以改变过程(这和只有一个步骤的工厂模式不同)
3、向客户隐藏产品内部的表现
4、产品的实现可以替换,因为客户只看到一个抽象的接口
5、与工厂模式相比,采用生成器模式创建对象的客户,需要具备更多的领域知识(需要知道步骤)
3、责任链模式
当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式
用途:
1、经常被使用在窗口系统中,处理鼠标和键盘之类的事件
优缺点:
1、将请求的发送者和接受者解耦
2、可以简化你的对象,因为它不需要知道链的结构
3、通过改变成员或者调动它们的次序,允许你动态地新增或者删除责任
4、但是并不保证请求一定会被执行,如果没有任何对象处理它的话,它可能会落到链尾端之外(既是优点也是缺点)
5、可能不容易观察运行时的特征,有碍于出除错
4、享元(蝇量)模式
如想让某个类的一个使用能用来提供许多“虚拟实例”,就使用享元模式
用途:
1、当一个类有许多的实例,而这些实例能被同一方法控制的时候,我们就可以使用享元模式
优缺点:
1、减少运行时对象实例的个数,节省内存
2、将许多“虚拟”对象的状态集中管理
3、缺点在于,一旦你实现了它,那么单个的逻辑实例将无法拥有独立而不同的行为