读书人

研磨设计形式之策略模式-4

发布时间: 2012-10-14 14:55:07 作者: rapoo

研磨设计模式之策略模式-4

?

?

3.3? Context和Strategy的关系

?

??????? 在策略模式中,通常是上下文使用具体的策略实现对象,反过来,策略实现对象也可以从上下文获取所需要的数据,因此可以将上下文当参数传递给策略实现对象,这种情况下上下文和策略实现对象是紧密耦合的。
??????? 在这种情况下,上下文封装着具体策略对象进行算法运算所需要的数据,具体策略对象通过回调上下文的方法来获取这些数据。
??????? 甚至在某些情况下,策略实现对象还可以回调上下文的方法来实现一定的功能,这种使用场景下,上下文变相充当了多个策略算法实现的公共接口,在上下文定义的方法可以当做是所有或者是部分策略算法使用的公共功能。
??????? 但是请注意,由于所有的策略实现对象都实现同一个策略接口,传入同一个上下文,可能会造成传入的上下文数据的浪费,因为有的算法会使用这些数据,而有的算法不会使用,但是上下文和策略对象之间交互的开销是存在的了。
??????? 还是通过例子来说明。


1:工资支付的实现思路
??????? 考虑这样一个功能:工资支付方式的问题,很多企业的工资支付方式是很灵活的,可支付方式是比较多的,比如:人民币现金支付、美元现金支付、银行转账到工资帐户、银行转账到工资卡;一些创业型的企业为了留住骨干员工,还可能有:工资转股权等等方式。总之一句话,工资支付方式很多。
??????? 随着公司的发展,会不断有新的工资支付方式出现,这就要求能方便的扩展;另外工资支付方式不是固定的,是由公司和员工协商确定的,也就是说可能不同的员工采用的是不同的支付方式,甚至同一个员工,不同时间采用的支付方式也可能会不同,这就要求能很方便的切换具体的支付方式。
??????? 要实现这样的功能,策略模式是一个很好的选择。在实现这个功能的时候,不同的策略算法需要的数据是不一样,比如:现金支付就不需要银行帐号,而银行转账就需要帐号。这就导致在设计策略接口中的方法时,不太好确定参数的个数,而且,就算现在把所有的参数都列上了,今后扩展呢?难道再来修改策略接口吗?如果这样做,那无异于一场灾难,加入一个新策略,就需要修改接口,然后修改所有已有的实现,不疯掉才怪!那么到底如何实现,在今后扩展的时候才最方便呢?
??????? 解决方案之一,就是把上下文当做参数传递给策略对象,这样一来,如果要扩展新的策略实现,只需要扩展上下文就可以了,已有的实现不需要做任何的修改。
??????? 这样是不是能很好的实现功能,并具有很好的扩展性呢?还是通过代码示例来具体的看。假设先实现人民币现金支付和美元现金支付这两种支付方式,然后就进行使用测试,然后再来添加银行转账到工资卡的支付方式,看看是不是能很容易的与已有的实现结合上。

2:实现代码示例
(1)先定义工资支付的策略接口,就是定义一个支付工资的方法,示例代码如下:?

?

/**

?* 支付工资的策略的接口,公司有多种支付工资的算法

?* 比如:现金、银行卡、现金加股票、现金加期权、美元支付等等

?*/

public interface PaymentStrategy {

??? /**

??? ?* 公司给某人真正支付工资

??? ?* @param ctx 支付工资的上下文,里面包含算法需要的数据

??? ?*/

??? public void pay(PaymentContext ctx);

}


(2)定义好了工资支付的策略接口,该来考虑如何实现这多种支付策略了。
??????? 为了演示的简单,这里先简单实现人民币现金支付和美元现金支付方式,当然并不真的去实现跟银行的交互,只是示意一下。
??????? 人民币现金支付的策略实现,示例代码如下:?

?

/**

?* 人民币现金支付

?*/

public class RMBCash implements PaymentStrategy{

??? public void pay(PaymentContext ctx) {

?????? System.out.println("现在给"+ctx.getUserName()

+"人民币现金支付"+ctx.getMoney()+"元");

??? }

}


同样的实现美元现金支付的策略,示例代码如下:?

?

/**

?* 美元现金支付

?*/

public class DollarCash implements PaymentStrategy{

??? public void pay(PaymentContext ctx) {

?????? System.out.println("现在给"+ctx.getUserName()

+"美元现金支付"+ctx.getMoney()+"元");

??? }

}

?

?(3)该来看支付上下文的实现了,当然这个使用支付策略的上下文,是需要知道具体使用哪一个支付策略的,一般由客户端来确定具体使用哪一个具体的策略,然后上下文负责去真正执行。因此,这个上下文需要持有一个支付策略,而且是由客户端来配置它。示例代码如下:

?

?

/**

?* 支付工资的上下文,每个人的工资不同,支付方式也不同

?*/

public class PaymentContext {

??? /**

??? ?* 应被支付工资的人员,简单点,用姓名来代替

??? ?*/

??? private String userName = null;

??? /**

??? ?* 应被支付的工资的金额

??? ?*/

??? private double money = 0.0;

??? /**

??? ?* 支付工资的方式策略的接口

??? ?*/

??? private PaymentStrategy strategy = null;

??? /**

??? ?* 构造方法,传入被支付工资的人员,应支付的金额和具体的支付策略

??? ?* @param userName 被支付工资的人员

??? ?* @param money 应支付的金额

??? ?* @param strategy 具体的支付策略

??? ?*/

??? public PaymentContext(String userName,double money,

PaymentStrategy strategy){

?????? this.userName = userName;

?????? this.money = money;

?????? this.strategy = strategy;

???? ?}

??? public String getUserName() {

?????? return userName;

??? }

??? public double getMoney() {

?????? return money;

??? }

/**

??? ?* 立即支付工资

??? ?*/

??? public void payNow(){

?????? //使用客户希望的支付策略来支付工资

?????? this.strategy.pay(this);

??? }

}

?

(4)准备好了支付工资的各种策略,下面看看如何使用这些策略来真正支付工资,很简单,客户端是使用上下文来使用具体的策略的,而且是客户端来确定具体的策略,就是客户端创建哪个策略,最终就运行哪一个策略,各个策略之间是可以动态切换的,示例代码如下:

?

?

public class Client {

??? public static void main(String[] args) {

?????? //创建相应的支付策略

?????? PaymentStrategy strategyRMB = new RMBCash();

?????? PaymentStrategy strategyDollar = new DollarCash();

??????

?????? //准备小李的支付工资上下文

?????? PaymentContext ctx1 =

new PaymentContext("小李",5000,strategyRMB);

?????? //向小李支付工资

?????? ctx1.payNow();

??????

?????? //切换一个人,给petter支付工资

?????? PaymentContext ctx2 =

new PaymentContext("Petter",8000,strategyDollar);

?????? ctx2.payNow();

??? }

}

?

运行一下,看看效果,运行结果如下:?

?

现在给小李人民币现金支付5000.0元

现在给Petter美元现金支付8000.0元

?

?

?

请接着看策略模式-5,其实是讲的一个主题,写在一个里面超长了,只好分成了两个,请见谅!

???

7 楼 chjavach 2010-07-01 to yinxiangbing兄:这确也是我的本意,设计模式的书不少,很多朋友也看了很多的书籍,可是他们还是不能“真正弄懂”设计模式,也不能把设计模式应用到实际的工作中去。
为什么呢?
我觉得有两点:一个就是对设计模式本身理解不到位;另一个就是缺乏实践经验。
因此,想在这个方面做些尝试,希望对大家有帮助。 8 楼 chjavach 2010-07-01 尤其是现在有一个现象,过多的关注语言的生动和故事性,一会儿三国、一会儿悟空的,跟实际应用环境差距太大,当然看完了也不能让人学会如何应用。
生动有趣对于培养学习兴趣,或是对大众进行知识普及,就是科普类的,还是有点用处的。但是,作为专业人员,专业书籍,应该更加关注内容的使用性和深入程度,毕竟这不是休闲小说,也不是科普入门,专业人员要的是真正学会并能应用这些知识,而不仅仅只是了解。
“介绍原子弹的科普书籍”,不管写得如何的浅显、生动、有趣,都只是给我们这些外行看的,看完了你就会造原子弹了吗?当然不能。 9 楼 tzm1984 2010-07-02 vtrtbb 写道请教:小李和Petter是在你知道的情况下,如果我有个花名册,有好多个人,你怎么判断呢???

1 小王
2 小李
3 小张
4 Petter
5 Tom


??????
兄弟 不管多少人,你肯定都会知道每个人的工资支付方式的。不然还混个毛啊
遍历就是了
人名 支付方式
小王 RMB
小李 RMB
小张 美元


。 10 楼 vtrtbb 2010-07-02 tzm1984 写道vtrtbb 写道请教:小李和Petter是在你知道的情况下,如果我有个花名册,有好多个人,你怎么判断呢???

1 小王
2 小李
3 小张
4 Petter
5 Tom


??????
兄弟 不管多少人,你肯定都会知道每个人的工资支付方式的。不然还混个毛啊
遍历就是了
人名 支付方式
小王 RMB
小李 RMB
小张 美元




那是不是要

PaymentStrategy strategy = null;


if 支付方式==美元
strategy = new DollarCash();
else if 支付方式==人民币
strategy = new RMBCash();

PaymentContext ctx1 = new PaymentContext("小李",5000,strategy );
//向小李支付工资
ctx1.payNow();


感觉侧率模式还是不能真正的解脱 if else ....

也有人说过用反射 11 楼 fromaust 2010-07-03 有搞头啊! 12 楼 mococa 2010-07-04 vtrtbb 写道tzm1984 写道vtrtbb 写道请教:小李和Petter是在你知道的情况下,如果我有个花名册,有好多个人,你怎么判断呢???

1 小王
2 小李
3 小张
4 Petter
5 Tom


??????
兄弟 不管多少人,你肯定都会知道每个人的工资支付方式的。不然还混个毛啊
遍历就是了
人名 支付方式
小王 RMB
小李 RMB
小张 美元




那是不是要

PaymentStrategy strategy = null;


if 支付方式==美元
strategy = new DollarCash();
else if 支付方式==人民币
strategy = new RMBCash();

PaymentContext ctx1 = new PaymentContext("小李",5000,strategy );
//向小李支付工资
ctx1.payNow();


感觉侧率模式还是不能真正的解脱 if else ....

也有人说过用反射

Map<String, PaymentStrategy> map = new HashMa<String, PaymentStrategy>();
map.put('人民币', new RMBCash());
map.put('人民币', new DollarCash);
PaymentContext ctx1 = new PaymentContext("小李",5000);
ctx1.setPaymentStrategy(map.get(ctx1.payType()));
//向小李支付工资
ctx1.payNow();

不知道这样能不能解决你的if else 困惑呢
13 楼 chjavach 2010-07-05 vtrtbb 写道tzm1984 写道vtrtbb 写道请教:小李和Petter是在你知道的情况下,如果我有个花名册,有好多个人,你怎么判断呢???

1 小王
2 小李
3 小张
4 Petter
5 Tom


??????
兄弟 不管多少人,你肯定都会知道每个人的工资支付方式的。不然还混个毛啊
遍历就是了
人名 支付方式
小王 RMB
小李 RMB
小张 美元




那是不是要

PaymentStrategy strategy = null;


if 支付方式==美元
strategy = new DollarCash();
else if 支付方式==人民币
strategy = new RMBCash();

PaymentContext ctx1 = new PaymentContext("小李",5000,strategy );
//向小李支付工资
ctx1.payNow();


感觉侧率模式还是不能真正的解脱 if else ....

也有人说过用反射


这个问题,你在策略模式-6里面也提出了,我在那里详细回答了,你可以去看一下 14 楼 terax 2010-10-12 我对Context和Strategy的关系的理解:

Strategy是算法的最高层次的抽象,Context是数据的最高层次的抽象。
任何接口都本能的需要Context,不知道这样理解对不对。

15 楼 chjavach 2010-10-14 terax 写道我对Context和Strategy的关系的理解:

Strategy是算法的最高层次的抽象,Context是数据的最高层次的抽象。
任何接口都本能的需要Context,不知道这样理解对不对。



不完全正确,Context不单单是数据的最高层次,它还可以提供公共方法,相当于是所有策略实现都可以使用的一个公共环境

读书人网 >软件开发

热点推荐