Jone Sun's Blog

用心发现,这个星球很美!

0%

前言

命令模式(Command): 把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。

每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。

命令允许请求的一方和接收请求的一方能够独立演化,从而具有以下的优点:

  • 命令模式使新的命令很容易地被加入到系统里
  • 允许接收请求的一方决定是否要否决请求
  • 能较容易地设计一个命令队列
  • 可以容易地实现对请求的撤销和恢复
  • 在需要的情况下,可以较容易地将命令记入日志

命令模式涉及到五个角色,它们分别是:

  • 客户端(Client)角色:创建一个具体命令(ConcreteCommand)对象并确定其接收者。

  • 命令(Command)角色:声明了一个给所有具体命令类的抽象接口。

  • 具体命令(ConcreteCommand)角色:定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。

  • 请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。

  • 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

    阅读全文 »

前言

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式(Observer): 又称发布-订阅模式(Publish-Subscribe:Pub/Sub)。它是一种通知机制,让发送通知的一方(被观察方)和接收通知的一方(观察者)能彼此分离,互不影响。

Java标准库虽然提供了java.util.Observer和java.util.Observable这两个类用于实现观察者模式,但是Java9开始已经废弃java.util.Observer和java.util.Observable这两个类, 实现观察者模式的时候不推荐使用:

此类和Observer接口已被弃用。 Observer和Observable支持的事件模型非常有限,Observable传递的通知顺序未指定,并且状态更改与通知不一一对应。 对于更丰富的事件模型,请考虑使用java.beans包。 为了在线程之间进行可靠且有序的消息传递,请考虑使用java.util.concurrent包中的并发数据结构之一。 有关反应式流样式的编程,请参阅Flow API。

阅读全文 »

前言

模板方法(TemplateMethod): 它的主要思想是,定义一个操作的一系列步骤,对于某些暂时确定不下来的步骤,就留给子类去实现好了,这样不同的子类就可以定义出不同的步骤。

模板类定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法的核心思想是:父类定义骨架,子类实现某些细节。模板方法是一种高层定义骨架,底层实现细节的设计模式,适用于流程固定,但某些步骤不确定或可替换的情况

为了防止子类重写父类的骨架方法,可以在父类中对骨架方法使用final。对于需要子类实现的抽象方法,一般声明为protected,使得这些方法对外部客户端不可见。

Java标准库也有很多模板方法的应用。在集合类中,AbstractList和AbstractQueuedSynchronizer都定义了很多通用操作,子类只需要实现某些必要方法。

阅读全文 »

前言

策略模式(Strategy): 定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类)即使用策略(指定一个策略或者使用默认策略)的上下文

它实际上指,在一个方法中,流程是确定的,但是,某些关键步骤的算法依赖调用方传入的策略,这样,传入不同的策略,即可获得不同的结果,大大增强了系统的灵活性。

策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

策略模式的核心思想是在一个计算方法中把容易变化的算法抽出来作为“策略”参数传进去,从而使得新增策略不必修改原有逻辑。通过扩展策略,不必修改主逻辑,即可获得新策略的结果。

策略模式在Java标准库中应用非常广泛,如Arrays.sort(T[] a, Comparator<? super T> c), 通过传入不同的Comparator实现不同的排序算法就是一种非常典型的策略模式

阅读全文 »

前言

享元模式(Flyweight): 主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。享元模式的设计思想是尽量复用已创建的对象,常用于工厂方法内部的优化。

如果一个对象实例一经创建就不可变,那么反复创建相同的实例就没有必要,直接向调用方返回一个共享的实例就行,这样即节省内存,又可以减少创建对象的过程,提高运行速度。

总是使用工厂方法而不是new操作符创建实例,可获得享元模式的好处。

在学习享元模式之前需要先了解一下 细粒度 和享元对象中的 内部状态、外部状态 这三个概念:

  • 内部状态:不随环境改变而改变的状态,内部状态可以共享,例如人的性别,不管任何环境下都不会改变
  • 外部状态:随着环境改变而改变的状态,不可以共享的状态,享元对象的外部状态通常由客户端保存,并在享元对象创建后,需要的时候传入享元对象内部,不同的外部状态是相互独立的。例如衣服和鞋子,人在不同的环境下会穿不同的衣服和鞋子,但是衣服和鞋子又是相互独立不受彼此影响的
  • 细粒度:较小的对象,所包含的内部状态较小

java中的string字符的不变性其实就是享元模式的应用!

阅读全文 »

前言

组合模式(Composite): 将多个对象组合在一起进行操作,常用于表示树形结构中。为了简化代码,使用Composite可以把一个叶子节点与一个父节点统一起来处理。

Composite模式使得叶子对象和容器对象具有一致性,从而形成统一的树形结构,并用一致的方式去处理它们

听起来有点不好理解,我们举个例子就好理解了: 在XML或HTML中,从根节点开始,每个节点都可能包含任意个其他节点,这些层层嵌套的节点就构成了一颗树。又例如我们开发GUI时用到的Android或者其他技术里的各个布局编写,每个布局都是独立的,但可以任意组合排列成开发者想要的大布局,即横向布局里可以放任意横向/纵向布局,纵向布局里也可以放任意横向/纵向布局,开发者先定义一个根布局,然后在内部添加任意布局

阅读全文 »

前言

桥接模式通过分离一个抽象接口和它的实现部分,使得设计可以按两个维度独立扩展

桥接模式(Bridge): 将抽象化与实现化解耦,使得二者可以独立变化。是为了避免直接继承带来的子类爆炸

像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了

阅读全文 »

前言

外观模式(Facade),又叫门面模式,是为了解决类与类之家的依赖关系的,给客户端提供一个统一入口,并对外屏蔽内部子系统的调用细节。像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度

很多Web程序,内部有多个子系统提供服务,经常使用一个统一的Facade入口,例如一个RestApiController,使得外部用户调用的时候,只关心Facade提供的接口,不用管内部到底是哪个子系统处理的。

阅读全文 »

前言

装饰器模式(Decorator),是一种在运行期动态给某个对象的实例增加功能的方法。

Decorator模式的目的就是把一个一个的附加功能,用Decorator的方式给一层一层地累加到原始数据源上,最终,通过组合获得我们想要的功能。

实际上Java标准库中对于IO流的处理就应用了装饰器模式: 通过FileInputStream获取原始文件流,如果需要增加缓冲功能就用BufferedInputStream包装下,如果还需要解压缩功能就再用GZIPInputStream包装下…, 无论包装多少次,得到的对象始终是InputStream。

使用Decorator模式实际上把核心功能和附加功能给分开了。核心功能指FileInputStream这些真正读数据的源头,附加功能指加缓冲、压缩、解密这些功能。

  • 如果我们要新增核心功能,就增加Component的子类,例如ByteInputStream。
  • 如果我们要增加附加功能,就增加Decorator的子类,例如CipherInputStream。

两部分都可以独立地扩展,而具体如何附加功能,由调用方自由组合,从而极大地增强了灵活性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
             ┌───────────┐
│ Component │
└───────────┘

┌────────────┼─────────────────┐
│ │ │
┌───────────┐┌───────────┐ ┌───────────┐
│ComponentA ││ComponentB │... │ Decorator │
└───────────┘└───────────┘ └───────────┘

┌──────┴──────┐
│ │
┌───────────┐ ┌───────────┐
│DecoratorA │ │DecoratorB │...
└───────────┘ └───────────┘
阅读全文 »

前言

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式是Adapter,也称Wrapper(所以如果看到源码中有以这两个结尾的类,那么大概率使用了适配器模式), java中实现适配器模式共有三种:

  • 类适配器: 当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可

  • 对象适配器: 当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。使用组合替代继承,故相比较类适配器,对象适配器更加常用

  • 接口适配器: 当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可

    阅读全文 »