定义: 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少各种算法类与使用算法类之间的耦合关系。
策略模式主要用一个类(context环境类)来承接上下文,配置维护一个对所有算法父类对象的引用。
策略模式遵循的结构图如下:
Context(应用场景):
1 需要使用ConcreteStrategy提供的算法。
2 内部维护一个Strategy的实例。
3 负责动态设置运行时Strategy具体的实现算法。
4 负责跟Strategy之间的交互和数据传递。
Strategy(抽象策略类):
1 定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
ConcreteStrategy(具体策略类):
1 实现了Strategy定义的接口,提供具体的算法实现。
下面列举一个开发过程中的实例进行讲解:
模块功能:UI界面通过不同的操作详细向服务端发送请求,并且处理相应结果。
1.请求apk信息,发送给UI。
2.请求apk、图片下载。
3.请求应用版本,通知UI是否可以更新。
4.请求查询信息。
对于上面同一个模块功能,下面将列举四个例子来进行比较:
(1)常规的操作方法。
(2)策略模式实现
(3)策略模式+简单工厂
(4)策略模式+反射机制
1)一般代码架构情形:
枚举类型: RequestChoise------请求类型
UI客户端: UIClient-------------请求发起、调用者
枚举类型RequestChoise.java
3 | public enum RequestChoise { |
5 | REQEUST_APK_IMG_DOWNLOAD, |
UIClient.java
01 | package com.zlc.nomal; |
03 | import com.zlc.all.RequestChoise; |
05 | public class UIClient { |
07 | public void questMsg(RequestChoise choise,String url){ |
10 | System.out.println( "1:请求apk信息" ); |
11 | System.out.println( "2:把数据通知给UI" ); |
12 | System.out.println( "3:…………" ); |
14 | case REQEUST_APK_IMG_DOWNLOAD: |
15 | System.out.println( "1:请求apk/图片下载" ); |
16 | System.out.println( "2:把数据保存到文件" ); |
17 | System.out.println( "3:通知UI已经下载成功或失败" ); |
18 | System.out.println( "4:…………" ); |
21 | System.out.println( "1:请求该应用版本信息" ); |
22 | System.out.println( "2:与本身应用的版本进行比较" ); |
23 | System.out.println( "3:通知UI显示是否更新对话框" ); |
24 | System.out.println( "4:…………………………" ); |
26 | case REQUEST_SEARCH_MSG: |
27 | System.err.println( "1:请求搜索框输入的信息" ); |
28 | System.out.println( "2:获取到的信息进行分页提示UI显示" ); |
29 | System.out.println( "3:……………………" ); |
这样写缺点: 1.客户端代码量多、繁杂。
2.客户端耦合性高,涉及到多个类。
3.客户端重复代码较多如:多次调用
2)使用策略模式:
刚我们提到策略模式涉及到三个角色:
环境(Context)角色:持有一个Strategy类的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
(1)抽象算法类:Request-------------提供公共接口
(2)具体算法类:RequestAPKMsg----请求apk详细信息
具体算法类:RequestDownload--请求下载资源
具体算法类:RequestSearchMsg-请求搜索结果
具体算法类:RequestVersion-----请求版本信息
(3)环境角色类:RequestContext------持有一个Request类的引用,提供具体算法调用接口
枚举类型: RequestChoise------请求类型
UI客户端: UIClient-------------请求发起、调用者
Request.java
6 | public interface Request { |
8 | public void requestFromeServer(String url); |
RequestAPKMsg.java
07 | public class RequestAPKMsg implements Request { |
10 | public void requestFromeServer(String url) { |
12 | System.out.println( "1:请求apk信息" ); |
13 | System.out.println( "2:把数据通知给UI" ); |
14 | System.out.println( "3:…………" ); |
RequestDownload.java
03 | * 具体算法类:RequestDownload |
06 | public class RequestDownload implements Request{ |
08 | public void requestFromeServer(String url) { |
10 | System.out.println( "1:请求apk/图片下载" ); |
11 | System.out.println( "2:把数据保存到文件" ); |
12 | System.out.println( "3:通知UI已经下载成功或失败" ); |
13 | System.out.println( "4:…………" ); |
RequestSearchMsg.java
03 | * 具体算法类:RequestSearchMsg |
06 | public class RequestSearchMsg implements Request{ |
08 | public void requestFromeServer(String url) { |
10 | System.err.println( "1:请求搜索框输入的信息" ); |
11 | System.out.println( "2:获取到的信息进行分页提示UI显示" ); |
12 | System.out.println( "3:……………………" ); |
RequestVersion.java
03 | * 具体算法类:RequestVersion |
06 | public class RequestVersion implements Request{ |
08 | public void requestFromeServer(String url) { |
10 | System.out.println( "1:请求该应用版本信息" ); |
11 | System.out.println( "2:与本身应用的版本进行比较" ); |
12 | System.out.println( "3:通知UI显示是否更新对话框" ); |
13 | System.out.println( "4:…………………………" ); |
RequestContext.java
01 | package com.zlc.stratery; |
03 | import com.zlc.all.Request; |
08 | public class RequestContext { |
09 | private Request request; |
10 | public RequestContext(Request request) { |
12 | this .request = request; |
14 | public void requestFromServer(String url){ |
15 | request.requestFromServer(url); |
UIClient.java
01 | package com.zlc.stratery; |
03 | import com.zlc.all.RequestAPKMsg; |
04 | import com.zlc.all.RequestChoise; |
05 | import com.zlc.all.RequestDownload; |
06 | import com.zlc.all.RequestSearchMsg; |
07 | import com.zlc.all.RequestVersion; |
09 | public class UIClient { |
11 | RequestContext context; |
12 | public void questMsg(RequestChoise choise,String url){ |
15 | context = new RequestContext( new RequestAPKMsg()); |
17 | case REQEUST_APK_IMG_DOWNLOAD: |
18 | context = new RequestContext( new RequestDownload()); |
21 | context = new RequestContext( new RequestVersion()); |
23 | case REQUEST_SEARCH_MSG: |
24 | context = new RequestContext( new RequestSearchMsg()); |
29 | context.requestFromServer(url); |
策略模式优点:
(1)
策略模式提供了管理相关的算法族的办法,策略类的等级结构定义了一个算法或行为族,恰当的使用继承可以把公共的代码移植带父类里面去,从而减少了代码的重复。
(2)
策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类(使用环境类一个子类对应一种算法类),每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
(3)
使用策略模式可以避免使用多重条件转移语句(if……else)。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
缺点:
(1)
客户端必须知道所有的策略类(
下面通过简单工厂+策略模式解决这个问题
),并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况(
可以通过反射解决这个问题
)。
(2)策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。
3)简单工厂+策略模式
把客户端的方法(switch部分)移植到环境类里面去。
环境角色类:RequestContext------持有一个Request类的引用,提供具体算法调用接口
UI客户端: UIClient-------------请求发起、调用者
其他同上
RequestContext
01 | package com.zlc.strategy.factory; |
03 | import com.zlc.all.Request; |
04 | import com.zlc.all.RequestAPKMsg; |
05 | import com.zlc.all.RequestChoise; |
06 | import com.zlc.all.RequestDownload; |
07 | import com.zlc.all.RequestSearchMsg; |
08 | import com.zlc.all.RequestVersion; |
13 | public class RequestContext { |
14 | private Request request; |
15 | public RequestContext(RequestChoise choise) { |
18 | request = new RequestAPKMsg(); |
20 | case REQEUST_APK_IMG_DOWNLOAD: |
21 | request = new RequestDownload(); |
24 | request = new RequestVersion(); |
26 | case REQUEST_SEARCH_MSG: |
27 | request = new RequestSearchMsg(); |
33 | public void requestFromServer(String url){ |
34 | request.requestFromServer(url); |
UIClietn.java
01 | package com.zlc.strategy.factory; |
03 | import com.zlc.all.RequestChoise; |
05 | public class UIClient { |
07 | RequestContext context; |
08 | public void questMsg(RequestChoise choise,String url){ |
09 | context = new RequestContext(choise); |
10 | context.requestFromServer(url); |
这样客户端的代码就更少了,也大大降低了代码的耦合性,客户端不需要涉及详细的算法类,更方便与维护。
4)反射+策略模式
环境角色类:RequestContext------持有一个Request类的引用,提供具体算法调用接口
UI客户端: UIClient-------------请求发起、调用者
其他同上
RequestContext.java
01 | package com.zlc.reflect; |
03 | import com.zlc.all.Request; |
08 | public class RequestContext { |
09 | private Request request; |
10 | public RequestContext(String fileName) { |
14 | clazz = Class.forName(fileName); |
15 | request = (Request)clazz.newInstance(); |
16 | } catch (Exception e) { |
22 | public void requestFromServer(String url){ |
23 | request.requestFromServer(url); |
UIClient.java
01 | package com.zlc.reflect; |
04 | public class UIClient { |
06 | RequestContext context; |
07 | public void questMsg(String url,String fileName){ |
08 | context = new RequestContext(fileName); |
09 | context.requestFromServer(url); |
这样,选择分支结构也去掉了。
策略模式(Strategy):它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的用户。
最后说一下策略模式和简单工厂之间的区别:
(1)策略模式的具体算法类对象是在使用者类里面进行创建的,然后传递给环境类(管理一个父类算法对象),简单工厂是通过使用者提供的条件在工厂类里面创建。
(2)策略模式具体是算法行为是通过环境类Context提供的接口调用,简单工厂是通过返回给调用者所需要的对象,然后由返回对象直接调用。