Java设计模式-策略模式

Wiki 上说,策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。

策略模式我感觉是非常简单的一种设计模式,应该也是一种比较基本的设计模式,在其它的设计模式中也能看到它的影子

模式定义

策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。

策略模式是一种对象行为型模式。

模式动机

完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。
在软件开发中也常常遇到类似的情况,实现某一个功能有多个途径,此时可以使用一种设计模式来使得系统可以灵活地选择解决途径,也能够方便地增加新的解决途径。

在软件系统中,有许多算法可以实现某一功能,如查找、排序等,一种常用的方法是硬编码(Hard Coding)在一个类中;如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法
当然也可以将这些查找算法封装在一个统一的方法中,通过 if…else… 等条件判断语句来进行选择。
这两种实现方法我们都可以称之为硬编码,如果需要增加一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。该类代码将较复杂,维护较为困难。

可以得出策略模式的使用场景

  • 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
  • 需要安全的封装多种同一类型的操作时。
  • 出现同一抽象多个子类,而又需要使用 if-else 或者 switch-case 来选择时。

通俗来说就是:

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  • 一个系统需要动态地在几种算法中选择一种。
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

策略模式:

  • 定义了一族算法(业务规则)
  • 封装了每个算法
  • 这族的算法可互换代替(interchangeable)

使用策略模式

其实策略模式我感觉在很多地方见过,说明应用还是很广泛的,貌似常用的回调技术也是这个模式的一种体现;这关键是依赖了一个设计原则:面向接口而不是面向实现

  • Context: 环境类
  • Strategy: 抽象策略类
  • ConcreteStrategy: 具体策略类

下面的栗子继续使用上次的吧,就设置情景为我要交朋友,但是交什么朋友是不确定的吧,有好多种(实现),虽然不太恰当,无所谓啦~~~

首先是抽象策略类,就是定义一个接口了,就是我要交的朋友的抽象

1
2
3
public interface Loli {
void hug();
}

具体的策略类,实现上面的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// No.1
public class StandardLoli implements Loli {
@Override
public void hug() {
System.out.println("要抱抱");
}
}
// No.2
public class LegitimateLoli implements Loli {
@Override
public void hug() {
System.out.println("(づ。◕‿‿◕。)づ");
}
}

下面是环境类,也就是使用场景,通常还可以在这个类中加入接口的一个 setter 方法,这样就可以动态的改变“策略对象”了:

1
2
3
4
5
6
7
8
9
10
11
public class MakeFriends {
private Loli mLoli;

public MakeFriends(Loli loli) {
mLoli = loli;
}

public void start() {
mLoli.hug();
}
}

然后就是测试了,也就是应该如何去使用,应该越看越熟悉了:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args) {
MakeFriends makeFriends;

makeFriends = new MakeFriends(new StandardLoli());
makeFriends.start();

System.out.println("---------------------------------");

makeFriends = new MakeFriends(new LegitimateLoli());
makeFriends.start();
}
}

评价

策略模式主要用来分离算法,根据相同的行为抽象来做不同的具体策略实现。
优点:

  • 结构清晰明了、使用简单直观。
  • 耦合度相对而言较低,扩展方便。
  • 操作封装也更为彻底,数据更为安全。
  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • 策略模式提供了管理相关的算法族的办法。
  • 策略模式提供了可以替换继承关系的办法。
  • 使用策略模式可以避免使用多重条件转移语句。

缺点:

  • 随着策略的增加,子类也会变得繁多。
    可以通过使用享元模式在一定程度上减少对象的数量。
  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

策略模式中的设计原则

  • 开闭原则(Open-Closed Principle,缩写为OCP)
    • 一个软件实体应当对扩展(例如对抽象层的扩展)开放,对修改(例如对抽象层的修改)关闭。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
    • 开闭原则的关键,在于抽象。策略模式,是开闭原则的一个极好的应用范例。
  • 里氏替换原则(Liskov Substitution Principle,缩写为LSP)
    • 一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉到基类对象和子类对象的区别。比如,假设有两个类,一个是 Base 类,一个是 Derived 类,并且 Derived 类是 Base 类的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b),那么它必然可以接受一个子类对象d,也即可以有 method1(d)。反之,则不一定成立
    • 里氏替换原则讲的是基类与子类的关系。只有当这种关系存在时,里氏替换关系才存在,反之则不存在。
    • 策略模式之所以可行的基础便是里氏替换原则:策略模式要求所有的策略对象都是可以互换的,因此它们都必须是一个抽象策略角色的子类。在客户端则仅知道抽象策略角色类型,虽然变量的真实类型可以是任何一个具体策略角色的实例

参考

https://www.zybuluo.com/pastqing/note/198333
https://github.com/simple-android-framework/android_design_patterns_analysis/tree/master/strategy/gkerison
http://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/strategy.html

评论框加载失败,无法访问 Disqus

你可能需要魔法上网~~