1、简述
有些常用的设计模式在实际编程时使用但总忘记其名称,这里做一个总结做下记录,用简洁提纲的方式帮助记忆,分以下几类:
2、创建型模式
提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
2.1、工厂模式 Factory
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
优:创建只需知道名称;增加一个产品便扩展一个工厂类;调用者无需关心具体实现只关心接口
劣:每一个产品都需要具体类和对象实现工厂,成倍增加了复杂度
场景:数据库访问、生产物品等
2.2、抽象工厂模式 Abstract Factory
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
优:易于交换产品系列,只需改变具体工厂即可使用不同产品配置;实例和客户端分离
劣:扩展困难,要增加一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码
场景:数据库更换
可以使用反射(Load)+配置文件(ini)结合来减少耦合
2.3、单例模式 Singleton
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
优:在内存里只有一个实例,减少了内存的开销;避免对资源的多重占用(比如写文件操作)
劣:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
场景:getInstance() 方法
2.4、建造者模式 Builder
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
优:建造代码与表示代码分离,易扩展;便于控制细节风险
劣:产品必须有共同点,范围有限制;如内部变化复杂,会有很多的建造类
场景:组合零件
2.5、原型模式 Prototype
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
优:性能提高;隐藏对象创建的细节,逃避构造函数的约束
劣:配备克隆方法需要对类的功能进行通盘考虑,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候;必须实现 Cloneable 接口
场景:复制简历
3、结构型模式
设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式
3.1、适配器模式 Adapter
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
优:可以让任何两个没有关联的类一起运行;提高了类的复用;增加了类的透明度;灵活
劣:过多地使用适配器,会让系统非常零乱,不易整体把握;双方都不易修改时再使用适配器模式
场景:翻译
3.2、桥接模式 Bridge
将抽象部分与实现部分分离,使它们都可以独立的变化。
优:抽象和实现的分离减少了耦合;优秀的扩展能力;实现细节对客户透明
劣:桥接模式的引入会增加系统的理解与设计难度,聚合关联关系建立在抽象层,要求开发者针对抽象进行设计编程
场景:不同手机适配
3.3、组合模式 Composite
将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
优:高层模块调用简单,让客户一致使用组合结构和单个对象;树形结构,节点自由增加
劣:其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则
场景:分公司架构
3.4、装饰器模式 Decorator
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
优:对已有功能动态添加更多功能;装饰模式是继承的一个替代模式;装饰模式可以动态扩展一个实现类的功能
劣:多层装饰比较复杂
场景:Avatar
3.5、外观模式 Facade
为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
优:不需知道内部复杂结构;不同层分离,减少系统相互依赖;提高灵活安全性
劣:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适
场景:基金
3.6、享元模式 Flyweight
运用共享技术有效地支持大量细粒度的对象。
优:减少对象的创建;降低系统的内存,使效率提高
劣:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱
场景:Blog、围棋
用唯一标识码判断区分
3.7、代理模式 Proxy
为其他对象提供一种代理以控制对这个对象的访问。
优:职责清晰;高扩展性;智能化
劣:由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢;实现代理模式需要额外的工作,有些代理模式的实现非常复杂
场景:以下几种常见场景
- 远程代理(为对象不同的地址空间提供局部代表)
- 虚拟代理 (根据需要创建开销很大的对象,通过代理存放实例化需要长时间的真实对象)
- 安全代理 (控制真实对象访问的权限)
- 智能指引 (调用真实对象时,代理处理一些其他事)
4、行为型模式
这些设计模式特别关注对象之间的通信
4.1、职责链模式 Chain of Responsibility
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
优:将请求的发送者和接收者解耦;简化对象,使对象不需要知道链的结构; 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任;增加新的请求处理类很方便
劣:不能保证请求一定被接收;系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用;不易观察运行时的特征
场景:审批
4.2、命令模式 Command
将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
优: 降低了系统耦合度;新的命令可以很容易添加到系统中去;可以执行撤销/重做/事务等处理
劣:可能导致某些系统有过多的具体命令类
场景:烤串
4.3、解释器模式 Interpreter
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
优:可扩展性好;增加了新的解释表达式的方式;易于实现简单文法
劣:可利用场景比较少;对于复杂的文法比较难维护;会引起类膨胀;采用递归调用方法。
场景:正则表达式、浏览器、机器人
4.4、迭代器模式 Iterator
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
优:支持以不同的方式遍历一个聚合对象;迭代器简化了聚合类;在同一个聚合上可以有多个遍历;在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
劣:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性
场景:公交买票
4.5、中介者模式 Mediator
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
优:网状结构,降低了类的复杂度,将一对多转化成了一对一;各个类之间的解耦;符合迪米特原则(两个类不必直接通信则通过第三方转发调用)
劣:中介者会庞大,变得复杂难以维护
场景:联合国
4.6、备忘录模式 Memento
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
优:提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;实现了信息的封装,使得用户不需要关心状态的保存细节
劣:消耗资源。如果类成员变量过多,则会占用比较大的资源,且每一次保存都会消耗一定的内存
场景:游戏存档
为了节约内存,可使用原型模式+备忘录模式
4.7、观察者模式 Observer
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
优:观察者和被观察者是抽象耦合的;建立一套触发机制(发布-订阅)
劣:如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化
场景:播放新闻
事件委托来解耦合
4.8、状态模式 State
允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
优: 封装了转换规则;枚举可能的状态,在枚举状态之前需要确定状态种类 ;将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为;允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块;可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
劣: 会增加系统类和对象的个数;状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,修改、增加新的状态类需要修改那些负责状态转换的源代码
场景:NPC行为随状态改变而改变
4.9、策略模式 Strategy
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
优:算法可以自由切换;避免使用多重条件判断;扩展性良好。
劣:策略类会增多;所有策略类都需要对外暴露
场景:收银台打折结算
4.10、模板模式 Template
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
优: 封装不变部分,扩展可变部分;提取公共代码,便于维护;行为由父类控制,子类实现
劣:每一个不同的实现都需要一个子类来实现,会导致类的个数增加,使得系统更加庞大
场景:子类共有的方法,且逻辑相同
4.11、访问者模式 Visitor
主要将数据结构与数据操作分离。
优:符合单一职责原则;优秀的扩展性;灵活性
劣:具体元素对访问者公布细节,违反了迪米特原则;具体元素变更比较困难;违反了依赖倒置原则,依赖了具体类,没有依赖抽象
场景:报表
5、六大原则
1、开闭原则(Open Close Principle)
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
是面向对象设计的基本原则之一。 任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)
这个原则是开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。
5、迪米特法则,又称最少知道原则(Demeter Principle)
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。