设计模式


设计模式

创建型设计模式

创建型设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

工厂模式

主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
使用场景:

  1. 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方
  2. 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时
  3. 设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。

抽象工厂模式

主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。

单例模式

主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
使用场景:要求生产唯一序列号,创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

建造者模式

主要解决:主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。

原型模式

主要解决:在运行期建立和删除原型。
如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。

何时使用:

  1. 当一个系统应该独立于它的产品创建,构成和表示时。
  2. 当要实例化的类是在运行时刻指定时,例如,通过动态装载。
  3. 为了避免创建一个与产品类层次平行的工厂类层次时。
  4. 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

使用场景:

  1. 资源优化场景。
  2. 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
  3. 性能和安全要求的场景。
  4. 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  5. 一个对象多个修改者的场景。
  6. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
  7. 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

结构型设计模式

适配器模式

主要解决:常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。是作为两个不兼容的接口之间的桥梁。
何时使用:有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式
如何解决:继承或依赖(推荐)

桥接模式

主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化
何时使用:实现系统可能有多个角度分类,每一种角度都可能变化。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合

过滤器模式

这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来

组合模式

组合模式是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次
主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦
何时使用:

  1. 您想表示对象的部分-整体层次结构(树形结构)。
  2. 您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
    如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。

装饰器模式

装饰器允许向一个现有的对象添加新的功能,同时又不改变其结构。
主要解决:我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
如何解决:将具体功能职责划分,同时继承装饰者模式。
何时使用:

  1. 扩展一个类的功能
  2. 动态增加功能,动态撤销

外观模式

外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口
主要解决:降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
何时使用:

  1. 客户端不需要知道系统内部的复杂联系,整个系统只需提供一个”接待员”即可
  2. 定义系统的入口
    如何解决:客户端不与系统耦合,外观类与系统耦合。
    使用场景:
  3. 为复杂的模块或子系统提供外界访问的模块

享元模式

享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。运用共享技术有效地支持大量细粒度的对象。
主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。实现与被代理的组合类

行为性设计模式

责任链模式

责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
如何解决:拦截的类都实现统一接口。
使用场景:

  1. 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
  2. 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
  3. 可动态指定一组对象处理请求。

    命令模式

    请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令
    主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
    如何解决:通过调用者调用接受者执行命令,顺序:调用者→命令→接受者
    使用场景:认为是命令的地方都可以使用命令模式

解释器模式

这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
主要解决:对于一些固定文法构建一个解释句子的解释器。
如何解决:构建语法树,定义终结符与非终结符。

使用场景:

  1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
  2. 一些重复出现的问题可以用一种简单的语言来进行表达。
  3. 一个简单语法需要解释的场景。

迭代器模式

这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。它支持以不同的方式遍历一个聚合对象。
主要解决:不同的方式来遍历整个整合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
使用场景:

  1. 访问一个聚合对象的内容而无须暴露它的内部表示。
  2. 需要为聚合对象提供多种遍历方式。
  3. 为遍历不同的聚合结构提供一个统一的接口。

中介者模式

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合
主要解决:对象与对象之间存在大量的关联关系,这样势必会导致系统的结构变得很复杂,同时若一个对象发生改变,我们也需要跟踪与之相关联的对象,同时做出相应的处理。
何时使用:多个类相互耦合,形成了网状结构。
如何解决:将上述网状结构分离为星型结构。

备忘录模式

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。
主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
何时使用:很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有”后悔药”可吃
如何解决:通过一个备忘录类专门存储对象状态。
使用场景:

  1. 需要保存/恢复数据的相关状态场景。
  2. 提供一个可回滚的操作。

观察者模式

当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

状态模式

在状态模式(State Pattern)中,类的行为是基于它的状态改变的
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
如何解决:将各种具体的状态类抽象出来

空对象模式

一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。

策略模式

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法
主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
使用场景:

  1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  2. 一个系统需要动态地在几种算法中选择一种。
  3. 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

    模板模式

    在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
    主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
    如何解决:将这些通用算法抽象出来。
    何时使用:有一些通用的方法。
    使用场景:
  4. 有多个子类共有的方法,且逻辑相同。
  5. 重要的、复杂的方法,可以考虑作为模板方法。

    访问者模式

    我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。
    主要解决:稳定的数据结构和易变的操作耦合问题
    如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
    何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,使用访问者模式将这些封装到类中。
    使用场景:
  6. 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
  7. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。

注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。

J2EE设计模式

六大原则

其他

适配器和桥接的区别

  1. 从意图上看
    适配器是为了复用已有接口的功能,而通过适配将已有接口功能引入到所需接口的一种模式,目的是能够结合。桥接模式是为了实现两个接口结合的多样化而设计的一种模式,目的是结合的更好。
  2. 从实现上看
    适配器是先定义了新接口,然后才与旧接口进行适配,即先接口后关系。
    桥接模式是先定义了一个桥(即两个接口之间的关系),然后通过每个接口的多个实现的不同组合达到其灵活性的目的,即先关系后组合。

文章作者: 彭峰
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 彭峰 !
  目录