“设计模式”是在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。通俗一点说,设计模式是在某种场合下对某个问题的一种解决方案。如果再通俗一点说,设 计模式就是给面向对象软件开发中的一些好的设计取个名字。23种设计模式来源于GoF总结常见的设计模式录入《设计模式:可复用面向对象软件的基础》。
设想有一个电子爱好者,虽然他没有经过正规的培训,但是却日积月累地设计并制 造出许多有用的电子设备:业余无线电、盖革计数器、报警器等。有一天这个爱好者决 定重新回到学校去攻读电子学学位,来让自己的才能得到真实的认可。随着课程的展开, 这个爱好者突然发现课程内容都似曾相识。似曾相识的并不是术语或者表述的方式,而 是背后的概念。这个爱好者不断学到一些名称和原理,虽然这些名称和原理原来他不知 道,但事实上他多年来一直都在使用。整个过程只不过是一个接一个的顿悟。《设计模式沉思录》
分类
- 创建型(5): 工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式
- 结构型(7): 适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式、
- 行为型(11): 职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式(发布/订阅模式)、状态模式、策略模式、模板方法模式、访问者模式
创建型模式
对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。
单例模式
- 定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点
- 透明单例模式;惰性单例模式
- 优点:共享性(唯一性, 全局可访问);节约资源,提高性能
- 缺点:扩展困难,单例类职责过重,滥用将带来负面影响
- 实例:Window, Cache, Modal
结构型模式
描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构。
适配器模式
- 定义: 将一个接口转换成客户希望的另一个接口使得接口不兼容的那些类可以一起工作
- 适配器模式的作用是解决两个软件实体间的接口不兼容的问题 适配器模式是一种 “亡羊补牢”的模式,没有人会在程序的设计之初就使用它
- 优点:通过新增一个适配类重用已有类;灵活性和扩展性比较好,可以方便的更换新增适配器
- 实例:XML-JSON
组合模式
- 定义: 组合多个对象形成树形结构已表示“整体-部分”的结构层次。它定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理
- 优点:可以忽略组合对象和个体对象之间的差别,统一处理,调用简单
- 缺点:组合对象和个体对象看不出差别来,使代码难以理解;可能通过组合模式创建了太多的对象而不被察觉
- 实例:文件系统,DOM结构
装饰模式
- 定义: 装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展
- 用AOP实现装饰函数
- 优点:更灵活;动态扩展功能,在运行时选择不同的装饰器,从而实现不同的行为
- 缺点:增加了系统的复杂性;灵活意味着更容易出错,排错比较麻烦
- 实例:http helper(拦截器)
享元模式
- 定义: 享元(flyweight)模式是一种用于性能优化的模式。要求区分内部状态和外部状态。
- 在享元模式中可以共享的相同内容称为内部状态(IntrinsicState),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State),由于区分了内部状态和外部状态,因此可以通过设置不同的外部状态使得相同的对象可以具有一些不同的特征,而相同的内部状态是可以共享的。
- 优点:极大减少内存中对象的数量;享元模式的外部状态相对独立,从而使得享元对象可以在不同的环境中被共享
- 缺点:需要分离出内部状态和外部状态,这使得程序的逻辑复杂化
- 实例:对象池
代理模式
- 定义: 给某一个对象提供一个代 理,并由代理对象控制对原对象的引用。并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
- 优点:保护代理,虚拟代理,远程代理
- 缺点:有些类型的代理模式可能会造成请求的处理速度变慢;实现代理模式需要额外的工作
- 实例:缓存代理(requirejs)
注意
有一些模式跟适配器模式的 结构非常相似,比如装饰者模式、代理模式和外观模式。这几种模式都属于“包 装模式”,都是由一个对象来包装另一个对象。区别它们的关键仍然是模式的意图
- 适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎样实 现的,也不考虑它们将来可能会如何演化。适配器模式不需要改变已有的接口,就能够 使它们协同作用。
- 装饰者模式和代理模式也不会改变原有对象的接口,但装饰者模式的作用是为了给对象 增加功能。装饰者模式常常形成一条长的装饰链,而适配器模式通常只包装一次。代理 模式是为了控制对对象的访问,通常也只包装一次。
行为型模式
在不同的对象之间划分责任和算法的抽象化。行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。
职责链模式
- 定义: 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
- 优点:解耦了请求发送者和 N 个接收者之间的复杂关系;灵活易扩展
- 缺点:不保证所有的请求都能够被处理
- 实例:作用域链、原型链、DOM节点中的事件冒泡
命令模式
- 定义: 将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作
- 优点:请求的发送者和接收者之间实现完全的解耦,发送者和接收者之间没有直接的联系,发送者只需要知道如何发送请求命令即可,其余的可以一概不管,甚至命令是否成功都无需关心;可以方便的实现操作的撤销和恢复
- 缺点:可能导致系统中会存在过多的具体命令类
- 实例:订餐;UI构建与逻辑开发分离
迭代器模式
- 定义: 指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示
- 优点:简化了聚合类; 可方便快速的遍历聚合对象
- 缺点:每个聚合类需要一个迭代器,类数目成对增加
- 实例:forEach, 迭代器,生成器
中介者模式
- 定义: 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
- 优点:简化对象之间的交互
- 缺点:在中介者包含了交互细节,可能会导致具体中介类非常复杂
- 实例:游戏服务器主机
观察者模式
- 定义: 对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知
- 优点:实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色
- 缺点:观察者太多的话会导致通知开销很大,有可能发生循环调用
- 实例:事件、MVC中的视图自动更新
状态模式
- 定义: 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类
- 优点:封装了转换规则,减少大块的if…else
- 缺点:会增加类或对象的个数,结构与实现都较为复杂
实例:电灯开关
策略模式
定义: 定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)
- 优点:用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为
- 缺点:客户端必须知道所有的策略类,并自行决定使用哪一个策略类;策略模式将造成产生很多策略类
- 实例:animation
模板方法模式
- 定义: 就是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
- 从大的方面来讲,模板方法模式常被架构师用于搭建项目的框架,架构师定好了框架的骨架, 程序员继承框架的结构之后,负责往里面填空
- 优点:基本的代码复用技术
- 缺点:子类个数增加
- 实例:文件上传