Fork me on GitHub

设计模式之策略模式

设计模式之策略模式

本篇是设计模式系列博客的第三篇,本篇主要讲一个设计模式中的一个行为形模式—策略模式。

一 策略模式定义

策略模式在百度百科中的解释是:

策略模式是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

策略模式的优点有:策略模式提供了管理相关的算法族的办法、策略模式提供了可以替换继承关系的办法、使用策略模式可以避免使用多重条件转移语句。

从上面我们可以清楚的了解到策略模式的一些基本特点:

  1. 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
  2. 在有多种算法相似的情况下,能够解决使用 if…else 所带来的复杂和难以维护。
  3. 当一个系统有许多的类的时候,并且区分它们的只是他们直接的行为。这时候我们可以考虑使用该模式。

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

应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。

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

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

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

策略模式实现

经过上面的分析我们大致可以得出策略模式的UML图如下:

image

这个模式涉及到三个角色:

环境(Context)角色:持有一个Strategy的引用。

抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

策略模式的典型代码如下:

抽象策略接口:

1
2
3
public interface TourismStrategy {
void travel();
}

具体的策略角色类:

1
2
3
4
5
6
public class CarTourismStrategy implements TourismStrategy {
@Override
public void travel() {
System.out.println("---滴滴滴---");
}
}
1
2
3
4
5
6
7
public class TrainTourismStrategy implements TourismStrategy {

@Override
public void travel() {
System.out.println("---呜呜呜---");
}
}

环境角色:

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

private TourismStrategy tourismStrategy;

public StrategyContext(TourismStrategy tourismStrategy){
this.tourismStrategy = tourismStrategy;
}

public void travel(){
tourismStrategy.travel();
}
}

策略模式案例

比如现在咱们老板想要生产一批鸭子!现在的这批鸭子有外观,有跑的功能。好的按照他的说法功能实现如下:

定义一个父类鸭子,有run方法,因为所有的鸭子run方法一样就写到父类中,而show方法因为不同的鸭子由具体的鸭子自己重写实现。

1
2
3
4
5
6
7
8
9
10
11
public abstract class Duck {
public void run(){
System.out.println("我在跑咯...");
}

/**
* 每个鸭子的形态不一样,所以定义成抽象方法由具体鸭子自己实现.
*/
public abstract void show();

}

然后看看一个具体的黑色的鸭子:

1
2
3
4
5
6
public class BlackDuck extends Duck {
@Override
public void show() {
System.out.println("黑色外观的鸭...");
}
}

然后在看一个具体的可达鸭:

1
2
3
4
5
6
public class KeDaDuck extends Duck{
@Override
public void show() {
System.out.println("黄色鸭子外观、宝可梦...");
}
}

可以看到他们都重写了自己的show 方法,因为外观不一样。
好了鸭子产品实现了,写个方法测试下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DuckDemo {
public static void main(String[] args) {
Duck duck =new KeDaDuck();

duck.run();
duck.show();

Duck duck1 =new BlackDuck();

duck1.run();
duck1.show();
}
}
1
2
3
4
5
6
7
 com.test.designPattern.demo.DuckDemo
我在跑咯...
黄色鸭子外观、宝可梦...
我在跑咯...
黑色外观的鸭...

Process finished with exit code 0

可以看到鸭子确实跑了 而且也能展示自己的形态了。好了产品实现了开始上线了。过了某些时候,老大气冲冲跑过来说,别人家也有这种鸭子了,为了提高竞争力,我们必须给鸭子加一些新的功能让她们有特色。好了又要我们加一个放技能的功能。好的,那好吧我们就加。

然后在父类Duck 类中加一个skill 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Duck {
public void run(){
System.out.println("我在跑咯...");
}

/**
* 每个鸭子的形态不一样,所以定义成抽象类由子类实现.
*/
public abstract void show();

/**
* 现在要加个需求为了适应产品的竞争。可达鸭我们发现它会放技能的。
*/
public abstract void skill();
}

具体的鸭子也要实现抽象方法:

黑色的鸭子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BlackDuck extends Duck {
@Override
public void show() {
System.out.println("黑色外观的鸭...");
}

/**
* 不会放技能什么都不做
*/
@Override
public void skill() {

}
}

可达鸭:

1
2
3
4
5
6
7
8
9
10
11
public class KeDaDuck extends Duck{
@Override
public void show() {
System.out.println("黄色鸭子外观、宝可梦...");
}

@Override
public void skill() {
System.out.println("发射水泡...biu biu biu ");
}
}

好了交给测试通过了,产品上线,确实竞争过别人了。但是此时我特么发现:不对啊!万一后面又要加其他的功能,比如(可达鸭)说话、变身,那我不得还得去改父类添加更多的方法,而且并不是所有的鸭子都有这个功能,没有的还得实现方法提供空实现,就像上面放技能一样只有可达鸭才有。所以到这里我们就发现问题了。

所以我们使用策略模式改造:

超类:定义公共的功能:

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
public  class Duck {

private Skill skill;



public Skill getSkill() {
return skill;
}

public void setSkill(Skill skill) {
this.skill = skill;
}




public void run(){
system.out.println("只要是鸭子都会跑");
}

//不同的鸭子放不同的技能这里不关心具体的实现是什么。
public void skill(){
skill.skill();
}
}

然后把经常变化的单独出来作为接口:

1
2
3
4
public interface Skill {

public void skill();
}
1
2
3
4
5
6
public class KedaSkill implements Skill {
@Override
public void skill() {
System.out.println("可达 可达。。。水泡");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class DuckDemo {
public static void main(String[] args) {
Duck duck =new Duck();

duck.setSkill(new KedaSkill());
duck.skill();


duck.setSkill(new BlackSkill());
duck.skill();
}
}

这样我们要加放技能的鸭子就实现这个接口,不加就不实现。易于扩展。如果后面我们有其他的功能只需要定义一个接口,增加一个接口的实现。不需要去修改以前的逻辑,只需要添加一些方法即可。

总结

策略模式的优点

  1. 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。

  2. 使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

策略模式的缺点

  1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
  2. 由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
-------------���Ľ�����л�����Ķ�-------------