您的位置:首页 > 编程语言 > Java开发

java 策略(Strategy)模式

2012-05-15 11:49 441 查看
策略(Strategy)模式

在实际的生产和生活中,经常有这样的事情:同一件事情在不同的情况下或针对不同的对象会有不同的做法。用程序设计术语来说就是:同样的接口,在不同的情况下需要使用不同的算法。而策略模式就是能够实现上述功能的一种设计模式,它可以在不影响客户端的情况下进行多个算法的替换。

例如,可能有好多人在上学期间做个私人家教,不同的学生收费是不一样的,教导一个小学生40元每小时,一个中学生60元每小时,而一个高中生就要90元每小时了。。如果要设计个软件来做这件事,那么通常首先考虑的是使用if…else或switch之类的结构来解决这个问题,例如如下代码:(代码不能运行,仅仅说明问题)

public class Client {

public static void main(String[] arg){
showPrice(studentypeType);

}

public static void showPrice(StudentType studentypeType){
if(studentypeType.equals(pupil)){//小学生
System.out.println("每小时40");
}else
if(studentypeType.equals(zhongxue)){//中学生
System.out.println("每小时60");
}else
if(studentypeType.equals(gaozhong)){//高中生
System.out.println("每小时90");
}
}

}


仔细分析一下就会知道,这个程序的可变性代码就是3个算法,因此可以先把这3个算法分别用3个类封装起来。由于算法就放在客户端,所以一旦某个计算方法发生变化,或者学生辅导类别是(例如幼儿园学生)就得修改客户端代码,显然它违背了"开-闭"原则,没有把可变性代码进行封装,所以这不是个好程序。

设计框架如下:



ChargeType是抽象类,客户面对的是抽象角色,而真正算法的实现是抽象角色的子类。也就是说,可变的算法已经与客户实现了隔离,从而提高了程序的可维护性。

程序源代码:(异常简单,仅仅说明问题)

public abstract class ChargeType {
public abstract void chargeInfo();

}

public class GaoZhongCharge extends ChargeType{

@Override
public void chargeInfo() {
System.out.println("每小时90元");//算法代码

}

}

public class XiaoXueCharge extends ChargeType{

@Override
public void chargeInfo() {
System.out.println("每小时40元");

}

}

public class ZhongXueCharge extends ChargeType{

@Override
public void chargeInfo() {
System.out.println("每小时60元");

}

}


客户端代码:

public class Client {

public static void main(String[] arg){
ChargeType chargeType = new XiaoXueCharge();//学生类型
chargeType.chargeInfo();//显示收费标准

}

}


由于这种模式可以很方便地在客户不知晓的情况下更换算法,所以被称为策略模式。

我们来看Thinking in java上的一个关于策略模式的例子



实例代码:

package com.jdf.java1;

import java.util.Arrays;

public class Apply {

public static void process(Processor processor,Object object){
System.out.println("Using Processor "+processor.name());
System.out.println(processor.process(object));
}

public static String string = "jia dong feng";

public static void main(String[] args) {
process(new Upcase(), string);
process(new DownCase(), string);
}

}

/**
* 不同的策略
* @author jia
*
*/
class Processor{
public String name(){
return getClass().getSimpleName();
}

Object process(Object input){
return input;
}
}

class Upcase extends Processor{
String process(Object input){
return ((String)input).toUpperCase();
}
}

class DownCase extends Processor{
String process(Object input){
return ((String)input).toLowerCase();
}
}

class Splitter extends Processor{
String process(Object input){
return Arrays.toString(((String)input).split(" "));
}
}


Apply.process()方法能够接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,称为策略设计模式。这类算法中包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。

状态设计模式和策略设计模式的区别:

1.大家可能会发现状态模式的结构和策略模式的结构完全一样,但是,它们的目的、实现、本质却是完全不一样的。还有行为之间的特性也是状态模式和策略模式一个很重要的区别,状态模式的行为是平行性的,不可相互替换的;而策略模式的行为是平等性的,是可以相互替换的。

2.

Strategy模式与State模式的结构形式几乎完全一样。但它们的应用场景(目的)却不一样

State模式重在强调对象内部状态的变化改变对象的行为,Strategy模式重在外部对策略的选择,策略的选择由外部条件决定,也就是说算法的动态的切换。但由于它们的结构是如此的相似,我们可以认为“状态模式是完全封装且自修改的策略模式”。

公认的事实:策略和状态模式是孪生兄弟。就象你所知道的,策略模式通过可互换的算法规则来创建非常成功的业务模式(wildly successful business)。不管怎么样,状态以非常高尚的方式帮助对象学习通过他们内部的状态来控制他们的行为。他总是无意中告诉他的对象客户,"跟着我重复就行了,我足够好,我足够聪明,..."
3.

1、策略模式

封装算法,让使用者可以更换不同的算法。变is a为has a 用组合来代替继承。

一旦置入算法,不会因使用者的内部状态来改变算法。

采用哪种算法,是由使用者掌握的。

2、状态模式

对象的内部状态决定它的行为。不同的状态有不同的行为,不同的行为又切换不同的状态。

当方法内出现很多if else时可以考虑使用状态模式。

状态模式重要的一点是在行为执行时,状态的变更。

4.http://www.uml.org.cn/sjms/201009195.asp
凡是涉及过设计模式的人都应该听过一些模式的名字,其中包括策略模式和状态模式.它们就是我今天要阐述的对象.之所以放在一起,是因为二者有某种关系可以帮助大家真正了解设计模式.为什么没有第三种,因为那样子太多了,说不清.

对于这两种模式官方的定义如下:
策略模式: 定义一系列算法,把它们一个个封装起来,并使它们能相互替换。使得算法可以独立于它的客户而变化。
状态模式: 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
这两个定义理解起来相当容易,二者大致可以概括为”对象根据不同的条件灵活选择不同的行为/方法”.这么看来策略模式和状态模式岂不是一样了?!现在看来确实是这么一回事.不过先别这么早的下结论,我们继续向下看.
这次看看二者的类图,如下:



图1.策略模式



图2.状态模式
这时候细心的人会发现这两种模式的UML类图完全一样.是不是觉得有点糊涂了?!这里先卖个关子,说点别的.
在平时大家学习设计模式的时候也许不会发现今天我给大家举出的情况,也不会觉得自己对设计模式的理解有什么不妥.不过你真的理解了吗?网上有很多关于设计模式的帖子,不管内容还是论调几乎都一样,随随便便的拿个例子或代码说明自己学会了设计模式.但是如果仔细比对,发现他们的内容几乎和教科书上一样,并且只包含其中的一部分.这样的结果是人云亦云,糊涂的继续糊涂,不懂得继续装懂.
好了,言归正传,回来继续说我们的策略模式和状态模式.通过上面说明,大家会发现不管从实现还是从设计,或者总结,二者几乎一模一样.那么二者真的是同一种模式吗?答案有两种,第一,是;第二不是.
答案一.”是”:是从某些开发或设计的角度看的,就像上面的过程.不过这只是表面现象;
答案二”不是”:很显然四人组比我们绝大多数人水平高的多,相比之下,我们出错的几率比他们高的多.那么这两种设计模式的区别到底在哪呢?
看过书大家都知道策略模式和状态模式都属于Gof23种设计模式中的行为模式.二者外表一样,只是二者的关注点不一样.策略模式关注行为的变化,状态模式关注对象状态的变化如何导致对象行为的变化.
策略模式中的两个角色:客户和策略.客户的职责是了解行为的差别,定义同一的接口.策略的职责是提供不同的实现,并根据客户的意图来选择.至于有多少种行为,一般很难知道,因为随着发展会有不同的变化加入.现实中一个简单的例子就是根据客户的要求选择不同的加密算法.当有新的加密算法出现时,客户需要知道这个变化,此时策略此可以灵活的选择使用新的算法.
状态模式一个形象的比喻是”饱了睡,饿了吃”.在这里”饱和饿”是两种状态,”睡和吃”是两种行为.另外一个典型的例子是银行账户.根据客户账户中余额的不同用户可以有不同的操作行为.这里要注意到,状态模式中状态与行为的对应关系.虽然不是一一对应,但潜藏了一些信息,那就是实际例子中行为与状态的有限和稳定,行为的唯一性.有限和稳定是指对象的行为一般就那几种,除非业务需求变动,否则不会发生改变.唯一性只是对象只有一个吃的行为,二不会有第二个吃的行为.
这时候策略模式与状态模式之间的区别就清楚了.
策略模式关注行为的变化,但归根结底只有一个行为,变化的只是行为的实现.客户不关注这些.当新增变化时对客户可以没有任何影响.
状态模式同样关注行为的变化,但这个变化是由状态来驱动,一般来说每个状态和行为都不同.新增的状态或行为一般与已有的不同,客户需要关注这些变化.
下面举两个实际中的例子.
一,在移动网络优化中,一般可以采用遗传算法,模拟退火算法和基于拓扑关系的算法.实际项目中,往往我们可以根据不同情况采用不同的算法,而客户对用什么算法往往并不关心.如果出现了第四种算法,工程师需要知道,而客户一样不需要知道.
二,基于GIS组件的二次开发,出了已有地图鼠标操作外,我们往往需要添加自己的鼠标行为,此时一个良好的设计是我们扩展鼠标的状态,扩展鼠标的行为,让后将他们对应起来.同时不影响原来的鼠标行为.基因
请仔细比较下面代码片断的细节差异.
1.策略模式 
2.状态模式
代码

public override void Action(string mouseState){
base.Action(mouseState);
if (mouseState == "Pan")
Pan();
else if (mouseState == "Select")
Select();
}
public void Pan() {/*平移地图*/}
public void Select(){/*选择图形*/}

代码
public void Topology(){
//通过拓Topology式计算A获取结果B
}
public void Gene(){
//通过Gene方式计算A获取结果B
}
public void Simulation(){
//通过Simulation方式计算A获取结果B
}
public void Strategy()
{
string flag = "";
if (flag == "Topology")
Topology();
else if (flag == "Gene")
Gene();
else if (flag == "Simulation")
Simulation();
//work complated
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: