策略模式

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。

何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

如何解决:将这些算法封装成一个一个的类,任意地替换。

关键代码:实现同一个接口。

应用实例:

  • 诸葛亮的锦囊妙计,每一个锦囊就是一个策略。

  • 旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。

  • JAVA AWT 中的 LayoutManager。

    优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

    缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

使用场景:

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

注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

实现:

首先创建一个接口用于运算:

1
2
3
4
5
6
7
8
9
public interface Strategy {
/**
* 运算接口
* @param num1
* @param num2
* @return
*/
public int doOperation(int num1, int num2);
}

它有4个实现类分别是加减乘除:

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 class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubstract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
public class OperationDivision implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 / num2;
}
}

然后我需要一个类来封装这个接口以及具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public Context(Strategy strategy) {
this.strategy = strategy;
}

public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}

public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationDivision());
System.out.println("10 / 5 = " + context.executeStrategy(10, 5));
}

要点:

1、知道OO基础,并不足以让你设计出良好的))系统。

2、良好的OO设计必须具备可复用、可扩充、可维护三个特性。

3、模式可以让我们建造出具有良好OO设计质量的系统。

4、模式被认为是历经验证的OO设计经验。

5、模式不是代码,而是针对设计问题的通用解决方案。你可把它们应该到特定的应用中!

6、模式不是被发明、而是被发现。

7、大多数的模式和原则,都着眼于软件变化的主题。

8、大多数的模式都允许系统局部改变独立于其他部分。

9、我们常把系统中会变化的部分抽出来封装。

10、模式让开发人员之间有共享的语言,能够最大化沟通的价值。

观察者模式

观察者模式定义了对象之间的一对多依赖,这样一来当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

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

主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

如何解决:使用面向对象技术,可以将这种依赖关系弱化。

关键代码:在抽象类里有一个 ArrayList 存放观察者们。

应用实例:

  • 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。

  • 西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。

    优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

    缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。

注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

实现:

首先创建观察者接口:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**

* 观察者,所有的观察者都需要实现update方法
* @author zzf
*
*/
public interface Observer {
public void update(Integer left,Integer right);
}
主题模式接口:

/**

* 主题接口

* @author zzf
*
*/
public interface Subject {

/**

* 用于注册观察者
* @param o
*/
public void registerObserver(Observer o);

/**

* 删除观察者
* @param o
*/
public void removeObserver(Observer o);

/**

* 当状态更新时这个方法会被调用通知所有观察者
*/
public void notifyObserver();
}
主题实现类,里面存放了一个维护Observer(观察者)的列表:

/**

* 主题的实现类

* @author zzf
*
*/
public class OperationData implements Subject{

private ArrayList<Observer> observersList=new ArrayList<Observer>();
private Integer left;
private Integer right;

@Override
public void registerObserver(Observer o) {
// TODO Auto-generated method stub
observersList.add(o);
}

@Override
public void removeObserver(Observer o) {
// TODO Auto-generated method stub
int i=observersList.indexOf(o);
if(i>=0) {
observersList.remove(i);
}
}

@Override
public void notifyObserver() {
// TODO Auto-generated method stub
for(Observer o:observersList)
o.update(left, right);
}

/**

* 当状态改变时调用notifyObserver,执行观察者方法
*/
public void Changed() {
notifyObserver();
}

public void setChanged(Integer left,Integer right) {
this.left=left;
this.right=right;
Changed();
}

}

下面实现三个Observer的实现类分别实现加减乘:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/* 

* 观察者实现类并将自身注册入Subject

* @author zzf

* @date 2018年10月17日 上午8:57:05
*/
public class AddDisplay implements Observer{

private Integer left;
private Integer right;
private Subject subject;

public AddDisplay(Subject subject) {
this.subject=subject;
subject.registerObserver(this);//将自身注册入主题
}

@Override
public void update(Integer left, Integer right) {
// TODO Auto-generated method stub
this.left=left;
this.right=right;
display();
}

public void display() {
System.out.println("Add:"+(left+right));
}

}
/*

* @author zzf

* @date 2018年10月17日 上午9:04:19
*/
public class SubstractDisplay implements Observer{

private Integer left;
private Integer right;
private Subject subject;

public SubstractDisplay(Subject subject) {
this.subject=subject;
subject.registerObserver(this);//将自身注册入主题
}

@Override
public void update(Integer left, Integer right) {
// TODO Auto-generated method stub
this.left=left;
this.right=right;
display();
}

public void display() {
System.out.println("Substract:"+(left-right));
}

}
/*
*观察者的实现类(乘法)

* @author zzf

* @date 2018年10月17日 上午9:11:32
*/
public class MultiplyDisplay implements Observer{

private Integer left;
private Integer right;
private Subject subject;

public MultiplyDisplay(Subject subject) {
this.subject=subject;
subject.registerObserver(this);//将自身注册入主题
}

@Override
public void update(Integer left, Integer right) {
// TODO Auto-generated method stub
this.left=left;
this.right=right;
display();
}

public void display() {
System.out.println("Multiply:"+(left*right));
}

}

下面创建一个测试类,将三个观察者的实现类注册入主题模式:

  • /* 
    
    * @author zzf 
    
    * @date 2018年10月17日 上午9:05:48 
      */
      public class Operation {
    
      public static void main(String[] args) {
          //创建主题
          OperationData operationData=new OperationData();
    
    
//将观察者的实现类注册入主题
AddDisplay addDisplay=new AddDisplay(operationData);
SubstractDisplay substractDisplay=new SubstractDisplay(operationData);
MultiplyDisplay multiplyDisplay=new MultiplyDisplay(operationData);

operationData.setChanged(5,5);
operationData.setChanged(10, 3);

}
}
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
46
47
48
49
50
51
52
53
54




2.通过Java API里面的java.util包内自带的Observer接口和Observable类实现观察者模式(详细请阅读源码实现原理很简单)

首先通过继承java.util.Observable来实现主题的实现类

```java
import java.util.Observable;

/*

* @author zzf

* @date 2018年10月17日 下午2:26:34
*/
public class OperationData extends Observable{

private Integer left;
private Integer right;

/**

* 当状态改变时调用notifyObserver,执行观察者方法
*/
public void Changed() {
setChanged();//将changed设置为true
notifyObservers();
}

public void setChanged(Integer left,Integer right) {
this.left=left;
this.right=right;
Changed();
}

public Integer getLeft() {
return left;
}

public void setLeft(Integer left) {
this.left = left;
}

public Integer getRight() {
return right;
}

public void setRight(Integer right) {
this.right = right;
}

}

然后通过实现java.util.Observer来实现观察类:

private Integer left;
private Integer right;
private Observable observable;

public MultiplyDisplay(Observable observable) {
    this.observable=observable;
    observable.addObserver(this);//将自身注册入主题
}

@Override
public void update(Observable o, Object arg) {
    // TODO Auto-generated method stub
    if(o instanceof OperationData) {
        OperationData operationData = (OperationData)o;
        this.left=operationData.getLeft();
        this.right=operationData.getRight();
        display();
    }
}

public void display() {
    System.out.println("Multiply:"+(left*right));
}
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
import java.util.Observable;
import java.util.Observer;

/*

* @author zzf

* @date 2018年10月17日 下午2:31:37
*/
public class AddDisplay implements Observer{

private Integer left;
private Integer right;
private Observable observable;

public AddDisplay(Observable observable) {
this.observable=observable;
observable.addObserver(this);//将自身注册入主题
}

@Override
public void update(Observable o, Object arg) {
// TODO Auto-generated method stub
if(o instanceof OperationData) {
OperationData operationData = (OperationData)o;
this.left=operationData.getLeft();
this.right=operationData.getRight();
display();
}
}

public void display() {
System.out.println("Add:"+(left+right));
}

}

要点:

1、观察者模式定义了对象之间一对多的关系。

2、主题(可观察者)用一个共同的接口来更新观察者。

3、观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。

4、使用哦此模式时,你可从被观察者处推或拉数据。

5、有多个观察者模式时,不可以依赖特定的通知次序。

6、要注意java.util.Observable实现上带来的一些问题(是一个类非接口,违反了针对接口编程)。

7、如果有必要,可以实现自己的Observable。

8、Swing中运用了大量观察者模式,许多GUI框架也是如此。

参考:https://www.w3cschool.cn/shejimoshi/strategy-pattern.html