观察者模式

观察者模式也叫发布订阅模式。

定义

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

观察者模式通用类图

实现

Subject

定义被观察者必须实现的职责,必须能够动态的增加、取消观察者。
它一般是抽象类或者实现类,仅仅完成作为被观察者必须实现的职责,管理观察者并通知观察者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class Subject{
private Vector<Observer> obsVector = new Vector<Observer>();

public void addObserver(Observer o){
this.obsVector.add(o);
}

public void delObserver(Observer o){
this.obsVector.remove(o);
}

public void notifyObservers(){
for(Observer o : this.obsVector){
o.update();
}
}

ConcreteSubject

具体的被观察者,定义被观察者自己的业务,同时定义对哪些事件进行通知。

1
2
3
4
5
6
public class ConcreteSubject extends Subject{
public void doBusiness(){
//...
super.notifyObservers();
}
}

Observer

观察者接收到消息后,对接收到的信息进行处理。

1
2
3
public interface Observer{
public void update();
}

ConcreteObserver

具体的观察者,不同的观察者在处理消息的时候的具体定义。

1
2
3
4
5
public class ConcreteObserver implements Observer{
public void update(){
//do something when invoked.
}
}

Use

1
2
3
4
ConcreteSubject subject = new ConcreteSubject();
Observer obs = new ConcreteObserver();
subject.addObserver(obs);
subject.doBusiness();

应用

优点

  • 观察者和被观察之间是抽象耦合
    如论增加观察者还是被观察者都非常容易扩展
  • 建立了一套规则的触发机制

缺点

效率缺陷,考虑采用异步方式通知。

广播链的问题 A -> B -> C

扩展

Java中的观察者

java 提供了java.util.Observer接口 和 java.util.Observable 类。
被观察者继承java.util.Observable, 观察者实现java.util.Observer.
但是在生产环境中考虑到性能一般不会用这种方式来实现。

生产中的观察者

一般使用MQ来异步处理
本地的如Disruptor

中介者模式

主要为了降低模块内部之间类的相互引用,防止出现系统或者模块内部过度耦合。

中介者模式也叫调停者模式,一个对象要和N个对象交流特别混乱,这个时候加入一个中心,所有的类都和中心交流,这样就变成了星型拓扑结构。

星型拓扑结构

定义

用一个中介对象封装一些列的对象交互,中介者使各对象不需要显示地相互调用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

中介者模式通用类图

实现

Mediator

抽象中介者角色,定义统一的接口,用于各同事角色之间的通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Mediator{
protected ConcreteColleague1 c1;
protected ConcreteColleague2 c2;
public ConcreteColleague1 getC1(){ return c1;}
public ConcreteColleague2 getC2(){ return c2;}
public void setC1(ConcreteColleague1 c1){
this.c1 = c1;
}
public void setC2(ConcreteColleague2 c2){
this.c2 = c2;
}

public abstract void doSomething1();
public abstract void doSomething2();
}

Concrete Mediator

具体中介者角色通过协调各同事角色实现协作行为,因为它必须依赖于各个同事角色。

1
2
3
4
5
6
7
8
9
10
public class ConcreteMediator extends Mediator{
public void doSomething1(){
super.c1.selfMethod1();
super.c2.selfMethod2();
}
public void doSomething2(){
super.c1.selfMethod1();
super.c2.selfMethod2();
}
}

Colleague

同事角色,每个同事角色都知道中介者角色,而且与其他同事角色通信时,一定要通过中介者角色协作。
一种是同事本身的行为叫自发行为,比如改变对象本身的状态,处理自己的行为,与其他同事类或者中介者
第二种是依赖行为,必须依赖中介者才能完成的行为。

1
2
3
4
5
6
public abstract class Colleague{
protected Mediator mediator;
public Colleague(Mediator mediator){
this.mediator = mediator;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ConcreteColleague1 extends Colleague{
public ConcreteColleague1(Mediator mediator){
super(mediator);
}

public void selfMethod1(){

}

public void depMethod1(){
super.mediator.doSomething1();
}
}

public class ConcreteColleague2 extends Colleague{
public ConcreteColleague2(Mediator mediator){
super(mediator);
}

public void selfMethod1(){

}

public void depMethod1(){
super.mediator.doSomething1();
}
}

应用

优点

减少类之间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,同时降低了类之间的耦合。

缺点

中介者模式的缺点是中介者会膨胀,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。

应用

机场调度中心

每一架飞机都和机场调度中心保持联系。

MVC框架

Controller做为一个中介者,把Model和View隔离开,协调MV协同工作,把M运行的结果和V代表的视图融合成一个前端可以展示的页面,减少MV的依赖关系。

媒体网关

中介服务

空对象模式

空对象模式时通过空代码实现一个接口或者抽象类的所有方法,以满足开发需求,简化程序。

定义

通过实现一个默认无意义的对象来避免null值的出现。为了避免程序中出现判断null的情况而产生的。

空对象模式通用类图

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Animal{
public void makeSound();
}

public class Dog implements Animal{
public void makeSound(){
System.out.println("Wang...");
}
}

public class NullAnimal import Animal{
public void makeSound(){}
}

解释器模式

解释器模式是一种按照规定语法进行解析的方案。

定义

给一门语言,定义它的文法的一种表示,并且定义一个解释器,该解释器使用该表示来解释语言中的句子。

比如Java语言,定义文法的表示是Java的语法。
javac编译器看作是解释器,解释器用来将Java“解释为” .class 格式的文件。

实现

AbstractExpression

抽象解释器,抽象解释器通常只有一个方法, 是生成语法集合的关键,每个语法集合完成指定语法的解析任务,通过递归调用的方式,最终由最小的语法但愿进行解析完成。
具体的解释任务由各个实现类完成。具体的解释器分别由TerminalExpression和Non-terminalExpression完成。

1
2
3
4
5
6
7
8
9
10
11
public abstract class Expression {

/**
* 解析公式和数值,其中var中的key是公式中的参数,value值是具体的数字。
* @param var
* @return
*/
public abstract int interpreter(HashMap<String,Integer> var);


}

TerminalExpression

终结符表达式
实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符的表达式,但是有多个实例,对应不同的终结符。表达式中每个终结符都在栈中产生了一个varExpression。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class VarExpression extends Expression {

private String key;

public VarExpression(String key) {
this.key = key;
}

@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(this.key);
}
}

NoneTerminalExpression

非终结符表达式
文法中每条规则都对应一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类,非终结表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public abstract class SymbolExpression extends Expression {

protected Expression left;

protected Expression right;

public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}

public class AddExpression extends SymbolExpression {

public AddExpression(Expression left, Expression right) {
super(left, right);
}

@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}

public class SubExpression extends SymbolExpression {

public SubExpression(Expression left, Expression right) {
super(left, right);
}

@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}

Context

环境角色,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Calculator {

private Expression expression;

public Calculator(String expStr) {
Stack<Expression> stack = new Stack<>();
char[] charArray = expStr.toCharArray();

Expression left = null;
Expression right = null;

for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '+':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new AddExpression(left, right));
break;
case '-':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
default:
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
this.expression = stack.pop();
}

public int run(HashMap<String, Integer> var) {
return this.expression.interpreter(var);
}
}

Use

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) throws IOException {
String expStr = getExpStr();
HashMap<String, Integer> var = getValue(expStr);
Calculator calculator = new Calculator(expStr);
System.out.println(calculator.run(var));
}

private static HashMap<String, Integer> getValue(String expStr) throws IOException {
HashMap<String, Integer> map = new HashMap<>();
for (char c : expStr.toCharArray()) {
if (c != '+' & c != '-') {
if (!map.containsKey(String.valueOf(c))) {
String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
map.put(String.valueOf(c), Integer.valueOf(in));
}
}
}
return map;
}

private static String getExpStr() throws IOException {
System.out.println("Input Expression");
return new BufferedReader(new InputStreamReader(System.in)).readLine();
}

应用

优点

扩展性良好,修改语法规则只需要修改相应的非终结符表达式就可以了。
如果扩展语法,则只要增加非终结符类就可以了。

缺点

解释器模式会引起类膨胀,每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可以能产生大量的类文件。
解释器模式采用递归调用方式,每个表达式需要知道最终的结果,必须一层层递归,无论是面向对象还是面向过程,递归都是在必要条件下才使用,导致调试复杂。

效率问题,解释器模式使用大量的循环和递归,效率时一个很大的问题。

使用场景

重复发生的问题可以解释器模式,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素相同,按照解释器模式终结符表达式相同,非终结符表达式需要定制。

一个简单语法需要解释的场景,解释器模式一般用来解析表标准的字符集,如SQL语法分析。

注意事项

尽量不要在重要的模块中使用解释器模式,维护成本太高。尽量使用shell、JRuby、Groovy来替代。

扩展

成熟的工具
Expression4J
MESP: Math Expression String Parser、
Jep.

备忘录模式

定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该状态之外保存这个状态。
这样以后就可以将该状态恢复到原来保存的状态。

备忘录模式通用类图

实现

Originator

发起人角色,记录当前时刻的内部状态,负责定义哪些属性备份范围的状态,负责创建和恢复备忘录数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Boy {

private String state = "";

public void change(){
this.state = " I Am So Sad.";
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

/**
* 可以创建多个备份
* @return
*/
public Memento createMemento(){
return new Memento(this.state);
}

/**
* 可以选择任意的备份进行恢复
* @param memento
*/
public void restore(Memento memento){
this.setState(memento.getState());
}
}

Memento

备忘录角色,负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Memento {

private String state = "";

public Memento(String state) {
this.state = state;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}
}

Caretaker

备忘录管理员角色,对备忘录进行管理、保存和提供备忘录。

1
2
3
4
5
6
7
8
9
10
11
12
public class Caretaker {

private Memento memento;

public Memento getMemento() {
return memento;
}

public void setMemento(Memento memento) {
this.memento = memento;
}
}

Use

1
2
3
4
5
6
7
8
9
Caretaker caretaker = new Caretaker();
caretaker.setMemento(boy.createMemento());
System.out.println("Now Boy Is : " + boy.getState());

boy.change();
System.out.println("Now Boy Is : " + boy.getState());

boy.restore(caretaker.getMemento());
System.out.println("Now Boy Is : " + boy.getState());

使用场景

  • 需要保存和恢复数据的相关状态场景
  • 提供一个可回滚的操作
  • 需要监控的副本场景中
  • 数据库连接的事务管理

注意事项

  • 备忘录的生命周期
    主动管理备忘录的生命周期,不实用就需要立即删除其引用。
  • 备忘录的性能
    要控制备忘录的历史记录数量,以及大对象的备忘录。

扩展

Clone方式的备忘录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Originator implements Cloneable {

private String state = "";

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public Originator createMemento(){
return this.clone();
}

public void restoreMemento(Originator originator){
this.setState(originator.getState());
}

@Override
protected Originator clone() {
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Caretaker {

private Originator originator;

public Originator getOriginator() {
return originator;
}

public void setOriginator(Originator originator) {
this.originator = originator;
}
}

多状态的备忘录模式

把整个对象转换成Map,当然也可以序列化、或者直接复制整个对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Memento {

private HashMap<String,Object> stateMap;

public Memento(HashMap<String, Object> stateMap) {
this.stateMap = stateMap;
}

public HashMap<String, Object> getStateMap() {
return stateMap;
}

public void setStateMap(HashMap<String, Object> stateMap) {
this.stateMap = stateMap;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashMap;

public class BeanUtils {

public static HashMap<String,Object> backupProp(Object bean){
HashMap<String,Object> result = new HashMap<>();

try{
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
String fieldName = descriptor.getName();
Method getter = descriptor.getReadMethod();
Object fieldValue = getter.invoke(bean, new Object[]{});
if (!fieldName.equalsIgnoreCase("class")){
result.put(fieldName, fieldValue);
}
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}


public static void restoreProp(Object bean, HashMap<String,Object> propMap){
try{
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor descriptor : descriptors) {
String fieldName = descriptor.getName();
if (propMap.containsKey(fieldName)){
Method setter = descriptor.getWriteMethod();
setter.invoke(bean, new Object[]{propMap.get(fieldName)});
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Originator {

private String state1 = "";
private String state2 = "";
private String state3 = "";

public String getState1() {
return state1;
}

public void setState1(String state1) {
this.state1 = state1;
}

public String getState2() {
return state2;
}

public void setState2(String state2) {
this.state2 = state2;
}

public String getState3() {
return state3;
}

public void setState3(String state3) {
this.state3 = state3;
}


public Memento createMemento(){
return new Memento(BeanUtils.backupProp(this));
}

public void restoreMemento(Memento memento){
BeanUtils.restoreProp(this, memento.getStateMap());
}
}

多备份的备忘录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Caretaker {

Map<String, Memento> map = new LinkedHashMap<>();

public void backup(String version, Memento memento){
map.put(version, memento);
}

public Memento get(String version){
return map.get(version);
}

public Map<String,Memento> getAll(){
return this.map;
}
}

优雅的实现

Bean 对象专注自己的业务,不需要关心备份和恢复业务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Original implements Cloneable {

private String state;

private String info;

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getInfo() {
return info;
}

public void setInfo(String info) {
this.info = info;
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

备份管理员负责版本的备份与恢复工作,职责明晰。
同时要注意历史版本次数,防止版本过多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Caretacker {

private int size = 10;

private Map<String, Original> histo = new LinkedHashMap<>();

public boolean backup(String key, Original o) {
if (histo.size() >= 10) {
return false;
}
try{
histo.put(key,(Original) o.clone() );
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}

public Original get(String k) {
return histo.get(k);
}
}

命令模式

命令模式的封装性非常好,把请求方(Invoker)和执行方(Receiver)分开,扩展性也非常好。

定义

将一个请求封装成一个对象,从而使得不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

命令模式通用类图

实现

Receiver

抽象接收者,定义每个接收者都必须完成的业务。

1
2
3
public abstract class Receiver{
public abstract void doSomething();
}

具体的Receiver

1
2
3
4
5
6
7
public class Concrete1Reveiver extends Receiver{
public void doSomething(){}
}

public class Concrete2Reveiver extends Receiver{
public void doSomething(){}
}

Command

不同的命令实现都需要继承该类,实现自己的execute逻辑。

1
2
3
public abstract class Command{
public abstract void execute();
}

具体的命令实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteCommand1 extends Command{
private Receiver receiver;
public ConcreteCommand1(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

public class ConcreteCommand2 extends Command{
private Receiver receiver;
public ConcreteCommand2(Receiver receiver){
this.receiver = receiver;
}
public void execute(){
this.receiver.doSomething();
}
}

Invoker

1
2
3
4
5
6
7
8
9
public class Invoker{
private Command command;
public void setCommand(Command command){
this.command = command;
}
public void action(){
this.command.execute();
}
}

Use

1
2
3
Invoker invoker = new Invoker();
invoker.setCommand(new ConcreteCommand1(new Concrete1Reveiver()));
invoker.action();

模式应用

优点

  • 解耦
    调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需要调用Command抽象类的execute方法即可,不需要了解是哪个接收者执行。
  • 可扩展性好
    Command子类可以非常容易的扩展,调用者Invoker和高层的模块Client不产生严重的代码耦合。
  • 与其他模式混合使用
    与责任链模式结合,实现命令族解析任务
    与模版方法结合,减少Command子类膨胀的问题。

缺点

Command的子类会随着命令的增多逐步膨胀。

命令的撤销

反命令

通过相反的命令来实现命令的撤销。

结合备忘录模式

结合备忘录模式还原最后状态,折中发昂发适合接收者为状态的变更情况,而不适合事件处理。

代码实例

假设有个录音机,录音机面板有三个按钮, play, rewind, stop。

需要根据三个按钮作出相应的反馈。

Receiver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AudioPlayer {

public void paly(){
System.out.println("Play");
}

public void rewind(){
System.out.println("Rewind");
}

public void stop(){
System.out.println("Stop");
}
}

Command

不同的Command调用不同的Receiver命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public abstract class Command {

protected static AudioPlayer audioPlayer = new AudioPlayer();

public abstract void execute();

}

public class PlayCommand extends Command {
@Override
public void execute() {
super.audioPlayer.paly();
}
}

public class RewindCommand extends Command {
@Override
public void execute() {
audioPlayer.rewind();
}
}

public class StopCommand extends Command {
@Override
public void execute() {
audioPlayer.stop();
}
}

Invoker

用于转发Command

1
2
3
4
5
6
7
8
9
10
11
public class KeyPad {
private Command command;

public void setCommand(Command command) {
this.command = command;
}

public void action() {
this.command.execute();
}
}

Use

1
2
3
4
5
6
7
KeyPad keyPad = new KeyPad();

keyPad.setCommand(new RewindCommand());
keyPad.action();

keyPad.setCommand(new StopCommand());
keyPad.action();

如果后期需要增加一个按钮,stopAndRewind(),直接增加一个Command子类即可,其他地方不需要动。

1
2
3
4
5
6
7
8
public class StopAndRewindCommand extends  Command {

@Override
public void execute() {
audioPlayer.stop();
audioPlayer.rewind();
}
}

模版方法模式

抽象类定义一组方法的执行顺序,具体的功能由子类来实现。
它仅仅使用了Java的继承机制,但使用非常广泛。
一般多个子类有公有的方法,并且逻辑基本相同的时候使用。

定义

定义一个操作中的算法框架,再将一些步骤延迟到子类中。使得子类可以不改变算法的结构就可以定义该算法的某些特定步骤。

模版方法模式通用类图

实现

基本方法

一些基本的操作,由子类实现具体的逻辑,在模版方法中被调用。

模版方法

模版方法可以有一个或者多个,一般是一个具体的方法,包含了基本方法的调用逻辑。

抽象模版类中包含了一个模版方法goToSchool和三个基本方法toSchool, study, goHome.
这样,每个学生继承该抽象类实现自己逻辑即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class StudentBehavor{
protected abstract void toSchool();
protected abstract void study();
protected abstract void goHome();
public void goToSchool(){
//do something.
toSchool();
// do something.
study();
// do something.
goHome();
// do something.
}

}

应用

优点

  • 封装不变部分,扩展可变部分
    把认为不变的部分算法封装到父类实现,可变部分通过继承来扩展。

  • 提取公共部分代码,便于维护

  • 行为由父类控制,子类实现
    基本方法由子类实现,子类可以扩展增加相应的功能,符合开闭原则。

缺点

在复杂的项目中,增加代码的阅读难度。

责任链模式

责任链模式的核心在链上, 链是有多个处理者组成的。就像流水线上的质检员。

定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

责任链模式通用类图

实现

抽象事件

抽象事件类型表明这类型的事件是需要被处理的。

1
2
3
public interface IEvent {
int getType();
}

具体的事件

不同类型的子类事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EventClick implements IEvent {
public int getType() {
return 1;
}
}
public class EventEnter implements IEvent {
public int getType() {
return 2;
}
}
public class EventBlank implements IEvent {
public int getType() {
return 3;
}
}

抽象的事件处理器

定义了只有Ievent类型的事件会被处理。

1
2
3
public interface IEventHandler {
boolean check(IEvent event);
}

具体的事件处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ClickEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() != 1){
return false;
}
System.out.println("Father check Ok. ");
return true;
}
}

public class EnterEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() == 2) {
System.out.println("Husband Ok.");
return true;
}
return false;
}
}

public class BlankEventHandler implements IEventHandler {

public boolean check(IEvent event) {
if (event.getType() != 3) {
return false;
}

System.out.println("Son Ok");
return true;

}
}

事件处理链

用于组织事件处理器,符合条件的处理器处理完成后或者没有处理器处理都会停止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EventHandlerChain {
private List<IEventHandler> eventChains = new ArrayList<IEventHandler>();
public void add(IEventHandler eventHandler) {
eventChains.add(eventHandler);
}
public boolean check(IEvent event) {
for (IEventHandler eventChain : this.eventChains) {
if (eventChain.check(event)) {
return true;
}
}
return false;
}

}

Use

可以随意组织责任链的顺序,然后检查需要检查的类型。

1
2
3
4
5
6
7
8
EventHandlerChain chain = new EventHandlerChain();
chain.add(new ClickEventHandler());
chain.add(new EnterEventHandler());
chain.add(new BlankEventHandler());

chain.check(new EventClick());
chain.check(new EventEnter());
chain.check(new EventBlank());

应用

优点

将请求和处理分开。请求着可以不用知道是谁处理的,处理者可以不用知道请求着的全貌。
两者解耦,提高了系统的灵活性。

缺点

性能问题, 最差的情况下,请求从头遍历到尾,链条特别长的时候,性能损耗是个大的问题。
因此在使用的过程中要注意责任链的长短。
调试不方便。

在广告系统中,由不同的事件来触发不同类型的广告时根据责任链模式来处理。
每个Handler可以专注自己的业务处理逻辑。具体Handler还可以抽象一个抽象类,把公用的逻辑写在抽象类里面,每个具体的实现类只负责业务的处理即可(优化为模版方法模式)。

迭代器模式

迭代器模式提供了遍历容器的方便性,容器只要管理增减元素即可,需要遍历的时候交由迭代器进行。

比如Set、Map、List等容器的iterator。

定义

迭代器是为容器服务的,它提供一种方法访问一个容器对象中的各个元素,而又不需要暴露该对象的内部细节。

迭代器模式通用类图

实现

Iterator

抽象迭代器负责定义访问和遍历元素的接口。
first() 获取第一个元素;
next() 访问下一个元素;
hasNext() 是否还有下一个;

1
2
3
4
5
public interface Iterator<E>{
E next();
boolean hasNext();
boolean remove();
}

ConcreteIterator

具体迭代器要实现迭代器接口,完成容器元素的遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteIterator implements Iterator<E>{
private Vector<E> v = new Vector<>();
public int cursor = 0;
public ConcreteIterator(Vector _vector){
this.v = _vector;
}
public boolean hasNext(){
return this.cursor != this.v.size();
}

public E next(){
return this.hasNext() ? this.v.get(this.cursor++) : null;
}

public boolean remove(){
this.v.remove(this.cursor);
return true;
}
}

Aggregate

抽象容器角色负责提供创建具体迭代器角色的接口, 必然提供一个类似于createIterator()的方法。

1
2
3
4
5
public interface Aggregate<E>{
void add(E e);
void remove(E e);
Iterator iterator();
}

Concrete Aggregate

具体容器实现容器接口定义的方法,创建出容纳迭代器的对象。

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteAggrate implements Aggregate<E>{
Vector v = new Vector();
public void add(E e){
this.add(e);
}
public Iterator<E> iterator(){
return new ConcreteIterator<>(this.v);
}
public void remove(E e){
this.remove(e);
}
}

访问者模式

访问者模式是一种集中规整模式,适合用于重构项目时(重构时需求已经清晰,原有的功能点也明确)
通过访问者模式可以很容易达到功能集中化。
还可以与其他模式混编建立一套自己的过滤器和拦截器。

定义

封装一些用于某种数据结构中的各个元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新操作。

访问者模式通用类图

角色

Visitor 抽象访问者

抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。

1
2
3
4
public interface IVisitor{
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}

ConcreteVisitor 具体访问者

影响访问者访问到一个类后该做什么事情。

1
2
3
4
5
6
7
8
public class Visitor implements IVisitor{
public void visit(ConcreteElement1 el1){
el1.doSomething();
}
public void visit(ConcreteElement2 el2){
el2.doSomething();
}
}

Element 抽象元素

接口或者抽象类,声明接受哪一类访问者访问。
抽象元素有两类方法, 一类是本身的业务逻辑。另一类是允许访问者来访问的方法。

1
2
3
4
5
6
public abstract class Element{
//业务逻辑
public abstract void doSomething();
//接受访问者
public abstract void accept(IVisitor visitor);
}

ConcreteElement 具体元素

实现accept方法, 通常是 visitor.visit(this)模式。

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteElement1 extends Element{
public void doSomething(){}
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
public class ConcreteElement2 extends Element{
public void doSomething(){}
public void accept(IVisitor visitor){
visitor.visit(this);
}
}

ObjectStructure结构对象

元素生产者,一般容纳在多个不同类、不同接口的容器,如List/Set/Map。

1
2
3
4
5
public class ObjectStructure{
public static Element createElement(){
return new Random().nextInt(10) > 5 ? new ConcreteElement1() : new ConcreteElement2();
}
}

使用

1
2
3
for(int i = 0; i < 10 ; i++){
ObjectStructure.createElement().accept(new Visitor());
}

优点

  • 符合单一职责原则
  • 优秀的扩展性
  • 灵活

缺点

  • 违背了迪米特法则,Element具体的细节需要公布给Visitor
  • 具体元素变更比较困难,元素增加/减少字段Visitor都需要修改
  • 违背了依赖倒置原则,访问者依赖的是具体的元素,而不是抽象元素。

使用场景

当一个对象结构包含很多类对象,它们有不同的接口,需要对这些对象实施一些依赖于其具体类的操作。

双分派

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class AbsActor{
public void act(Role role){
System.out.println("Actor can act any role.");
}
public void act(KungFuRole role){
System.out.println("Actor can act Kungfu Role.");
}
}

public class YongActor extends AbsActor{
public void act(KungFuRole role){
System.out.println("Yong Man Lov Act KungFu Role.");
}
}

public class OldActor extends AbsActor{
public void act(KungFuRole role){
System.out.println("Old Man Can Not Act KungFu Role.")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Role{
public void accept(AbsActor actor);
}
public class KungFuRole extends Role{
public void accept(AbsActor actor){
actor.act(this);
}
}
public class IdiotRole extends Role{
public void accept(AbsActor actor){
actor.act(this);
}
}
1
2
3
4
AbsActor actor = new OldActor();
Role role = new KungFuRole();
actor.act(role); //oldActor.act(Role)
actor.act(new KungFuRole()); //oldActor.act(KungFuRole);
1
2
3
4
AbsActor actor = new OldActor();
Role role = new KungFuRole();
role.accept(actor); //KungFuRole.accept(OldActor);
//OldActor.act(KungFuRole)