Java Design Patterns

Posted on By Vivian Sun

简介

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

一、 设计模式的分类

总体来说设计模式分为三大类:

  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 (红色)

  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 (绿色)

  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 (蓝色)

其实还有两类:并发型模式和线程池模式。

关系图:

二、 设计模式的六大原则

1. 开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。程序拓展的时候,不能去修改原有的代码。需要使用接口和抽象类。

这也是为了使程序的扩展性好,易于维护和升级。

2. 里氏代换原则(Liskov Substitution Principle)

任何基类可以出现的地方,子类一定可以出现。它是对实现抽象化的具体步骤的规范

3. 依赖倒转原则(Dependence Inversion Principle)

针对接口编程,依赖于抽象而不依赖于具体。

4. 接口隔离原则(Interface Segregation Principle)

使用多个隔离的接口,比使用单个接口要好降低类之间的耦合度

5. 迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6. 合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。

核心:降低依赖,降低耦合。

Java的23中设计模式

Code示意可以直接参看 Java开发中的23种设计模式详解

创建型模式 - 5种

1. 工厂方法模式(Factory Method)- 想要什么产品,自己选

核心:好几个类实现同一个接口,有个工厂类来管理,依据要求调用哪个实例的方法。

因此,扩展的话只需要增加类和调整Factory的逻辑,对外的接口可以不变

工厂模式分:普通工厂模式和多个工厂方法模式

它们的差别在于多个工厂提供了更多方法,免去了普通工厂模式传递参数的risk

麦当劳的点餐,可以点可乐,汉堡,也可以点套餐,这个可以认为是工厂模式。 老板不需要关心各个产品实例对象

2. 抽象工厂模式(Abstract Factory)- 想要多少产品,自己加

上面的工厂类,在拓展的时候需要修改Factory的逻辑,这就违反了闭包院子。

抽象工程模式的核心:创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码

其实就是把Factory再做一次优化,让他们都实现同一个接口(比如IProvider)。

之后需求要新增Factory(类似菜单),就直接增加Factory类,同时他们对外的接口不会变化都是IProvider的方法

这样就不用修改到Factory的类的方法了。

http://dl.iteye.com/upload/attachment/0083/1185/34a0f8de-16e0-3cd5-9f69-257fcb2be742.jpg

3. 单例模式(Singleton)- 我就一个,线程安全

比较简单,直接看之前写过的这里 Vivian’s Blog Android设计模式

4. 建造者模式(Builder)- 可选参数构建

有时候类创建的时候参数是可选的,那怎么动态的设定参数来创建类,这就是Builder

因为类里面的方法会返回类本身.

也可以参考上面的地址

5. 原型模式(Prototype)- 深浅复制

核心:复制、克隆,产生一个和原对象类似的新对象。

在Java中,复制对象是通过clone()实现,即实现Cloneable接口,最终调用的Object的clone()方法

这里要注意浅复制和深复制

深复制跟浅复制的差别:深复制,不论是基本数据类型还有引用类型,都是重新创建的。 就是深复制进行了完全彻底的复制,而浅复制的引用类型还是原对象所指向的

结构型模式 - 7种

6. 适配器 - 适配一下,换一个人

核心:将某个类的接口转换成客户端期望的另一个接口,目的不修改原有Code同时针对新需求做匹配。

主要分为三类:类的适配器模式,对象的适配器模式,接口的适配器模式


(1)类的适配器模式

创建一个类Wrapper实现接口ITargetable,继承或持有Source类实例,可以在类里面做逻辑上处理符合外部的需求。 这样Targetable接口的还具有了Source类的功能

这样Source类不修改,我们仅增加接口Targetable和Wrapper类做逻辑适配。


(2)对象的适配器模式

Wrapper类持有目标类Source的实例,实现接口ITargetable。需求变动需要修改Adapter类的源码。

作用跟第一类差不多,只是实现不同


(3)接口的适配器模式

主要是避免实现不必要的接口。

新增抽象类Wrapper实现接口Sourceable ,其他类只要继承Wrapper,实现自己需要的接口就可以了。

不用像之前一样要实现接口的所有方法,而且Sourceable一旦修改大家全部都要修改。现在只需要修改Wrapper类

使用场景:

  • 类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
  • 对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
  • 接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

7. 装饰模式(Decorator)- 装饰一下还是自己

核心:给一个对象增加一些新的功能,而且是动态的。

我们不能修改之前对象,因此我们要:

  • 新增一个Decorator跟原来的Source 类实现同一个接口,这是为了提供避免修改外部调用方法
  • Decorator持有Source的实例,这是为了在内容对Source的类的装饰

这个跟适配器的对象适配器模式有些类似,只是他是针对原有类做装饰,装饰后还是同一个类型。但是适配器是适配成另外一个接口类型。

简单说:装饰模式只是在原有的上面装饰,还是原来的东西。但是对象的适配模式是做了一个新东西

装饰器模式的应用场景:

  • 需要扩展一个类的功能。
  • 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

缺点:产生过多相似的对象,不易排错!

8. 代理模式(Proxy)- 像深层的装饰模式

核心: 多一个代理类出来,替原对象进行一些操作

个人感觉跟装饰模式有些类似,不过装饰模式感觉还是以Source为主,只是做一些修饰。代理模式更强势一些,甚至都可以不去调用Source的方法。

跟对象的适配也有些共通点,不过对象的适配不需要Source和Proxy实现同一个接口。

代理模式的应用场景:

如果已有的方法在使用的时候需要对原有的方法进行改进,可以使用代理模式。

9. 外观模式(Facade)- 解耦,搭配单例实现类似Manager,or MVP中的P

核心:外观模式是为了解决类与类之家的依赖关系

比如User类会用到N多类,如果我不想User类跟N多类做耦合,可以在加一层中间类来持有N多类的实例。解除User和N多类的耦合。

个人感觉就像是我不想跟各色各样的人车上关系,然后找了个小弟做统筹。我只跟小弟联系。其他跟管理上的不越级管理有点类似。

以后修改的话需要修改到Computer代码,其实这里还是可以优化下,Computer在实现一个接口,User使用接口的实例,这样如果要改动就可以新增类来做修改了。

10. 桥接模式(Bridge)- 事物,实现分离。适合客制化案例

核心:把事物和其具体实现分开。桥支持有接口的实例,具体的实现交给外部

  • 有个Bridge类持有接口ISource实例,外部有很多类SouceSub可以实现这个接口
  • Bridge提供get,set方法来设置和获取ISource的实例
  • Bridge中的方法只是去调用外部类的方法

如果需求变动需要新增SourceSub,只要新增类实现ISource,就可以了,Bridge不需要修改

个人感觉挺适合不同场景的有不同实现,但是对外只有一个接口的情况。

比如我们的Contact项目,需要依据不同过的专案做客制化。可以在一开始定义,判定专案名称,然后传递给Bridge,外部只要调用Bridge的方法即可

11. 组合模式(Composite)- 部分整体模式,逻辑设计

核心:也称作部分-整体模式,在处理类似树形结构的问题时比较方便

个人感觉更偏向于需求逻辑上的设计

12. 享元模式(Flyweight)- 有共有属性的对象共享

元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

线程池设计多会用到。

  • 有个工厂FlyWeightFactory负责创建和管理享元单元
  • 当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象

适用于共享的对象要有一些共有的属性。

比如数据库连接池的url、driverClassName、username、password及dbname

这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

行为型模式 - 11种

13. 策略模式(strategy)- 接口封装统一方法

核心:策略(算法)的变化不会影响外部的使用

  • 设计一个接口,提供统一方法
  • 多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数

个人感觉有像是桥接模式的一部分

14. 模板方法模式(Template Method)- 类的继承 + 抽象类对子类的调用

核心:类的继承 + 调用抽象类实现对子类的调用

  • 一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法
  • 定义一个类,继承该抽象类,重写抽象方法
  • 通过调用抽象类,实现对子类的调用

其实把策略模式中的接口换成抽象类,就有模板方法模式。 这里还可以联系到接口适配模式,可以自己感觉看看(抽象类实现接口)

15. 观察者模式(Observer)- 类和类之间的关系:有信通知我们

核心:订阅和通知。属于类和类之间的关系,不涉及到继承。当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

类似于邮件订阅和RSS订阅,到有新信息时会通知。

16. 迭代子模式(Iterator)- 类和类之间的关系:顺序访问聚集中的类对象,类似集合类

核心:顺序访问聚集中的对象

17. 责任链模式(Chain of Responsibility)- 类和类之间的关系:我知道我处理完交给下一个谁,不知道后面的

核心:多个对象,每个对象持有对下一个对象的引用,形成一条链

  • 请求在这条链上传递,直到某一对象决定处理该请求。
  • 发出者并不清楚到底最终那个对象会处理该请求

责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。

18. 命令模式(Command)类和类之间的关系:接到命令去做,不关心哪儿来的

核心: 包含发出命令者,命令,和执行者。其中发出者和执行者之间解耦,实现请求和执行分开。比如Struts,网络中开发中用的很多

  • 发命令的持有命令的对象
  • 命令持有执行者的对象

19. 备忘录模式(Memento)类和类之间的关系:备份某个状态下的对象实例

核心:保存一个对象的某个状态,以便在适当的时候恢复对象

  • Original原始类里面保存各属性value及创建一个备忘录类
  • Memento备忘录类,所有需要备份的属性都在这里
  • Storage是存储备忘录的类,持有Memento类的实例,可以存储获取某个状态的对象实例

20. 状态模式(State)- 告诉我状态,我会自己找路

核心:当对象的状态改变时,同时改变其行为。每个状态对应不同的操作

  • State状态类,包含各个状态定义和对应方法
  • Context类持有State类实例,封装了依据不同状态调用State中的对象方法

User只需关系State的状态,不需要考虑调用哪个方法,Context已经封装。

个人感觉跟桥接很有些神似,只是这里方法和状态都放到State里面。而且传入的是State,桥接传入的类的实例

21. 访问者模式(Visitor)

把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。

访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。

  • 一个Visitor类,存放要访问的对象
  • Subject类,accept方法,接受将要访问它的对象,getSubject()获取将要被访问的属性,

访问者模式适用于数据结构相对稳定算法又易变化的系统。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。

访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。

访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。

其缺点就是增加新的数据结构很困难。

22. 中介者模式(Mediator)- 解耦,User直接解耦,都找中介Mediator

核心:降低类类之间的耦合

  • User类统一接口,User1和User2分别是不同的对象,二者之间有关联
  • Mediator持有N多User实例,用来实现对N多User的操作

各个User间独立,解耦

个人刚觉有点类似有耦合关系的外观者模式

23. 解释器模式(Interpreter)

总结

在构建框架时,如果需求变动,能否做到不修改当前Code,只是新增类或接口来实现。

对修改封闭,对接口开放。

Reference