第二十二章 还钱——策略模式
22.1 还钱
时间:1月7日 地点:大B家 人物:大B和大B的朋友小王
小王两年前借大B的钱至今未还,正好这天小王来大B家做客(正是要帐的好机会)。
首先旁敲侧击法
大B:“前两天我见到咱们的老同学小李了,他还了我1000元钱,说是两年前借的,他要是不还呀,我都忘了。”
小王:“是呀!小李的记性就是好,比我强多了。”
居然没奏效,下一招诱蛇出洞
大B:“听说你最近生意不错呀!怎么样成本都收回来了嘛?债务都还上了吗?”
小王:“还算红火,债务基本上还清了。”
看来只能单刀直入了
大B:“那你借我的2000元也该还了吧!”
22.2 策略模式
大B:“是啊!难呐!这就是我要讲的策略模式了。”
小A:“喔?策略模式?什么是策略模式啊?”
小A:“策略模式就是定义一系列的算法,把他们一个个封装起来,并且使它们可相互替换。Strategy模式使算法可独立于使用它的客户而变化。”
22.3 策略模式涉及的角色
小A:“它所涉及到哪些角色呢?”
大B:“策略模式中分成三种角色。抽象策略角色:通常用一个抽象类或者接口来实现,主要是定义这个算法所完成的功能。具体策略角色:包装了相关算法和行为。环境角色:持有策略类的引用。”
下面我们还是看一个小例子,很多时候看代码更能够懂得其中的意思,不是那么抽象,这个例子要实现的功能是加减乘除。
首先建立抽象策略角色:
再建立具体策略角色:四个Add.java、Sub.java、Div.java、Multi.java
OK!抽象策略角色和具体策略角色都已经建立成功了,现在来建立环境角色
都建立好了测试一下
测试成功
点评策略模式:
策略模式的优点:
提供管理相关算法族的办法
提供可替代继承关系的办法
避免了使用多重条件判断语句
策略模式的缺点:
客户端必须知道所有的策略类,自己去决定使用那一个
造成很策略类
22.4 购物车系统
大B:“假设现在要设计一个贩卖各类书籍的电子商务网站的购物车(Shopping Cat)系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。”
小A:“一般会有哪些情况哩?”
大B:“比如:1、站可能对所有的儿童类图书实行每本一元的折扣;2、算机类图书提供每本7%的促销折扣,而对电子类图书有3%的折扣;3、余的图书没有折扣。4、还会有新的打折策略。由于有这样复杂的折扣算法,使得价格计算问题需要系统地解决。”
方案一:业务逻辑放在各具体子类
问题:每个子类必须都各自实现打折算法,即使打折算法相同。所以code reuse不好
方案二:
toSalePrice方法是比较容易change的地方,如果策略复杂用if判断比较乱,并且策略修改或增加时需改变原代码。
方案三:策略模式
22.5 策略模式的优点、缺点
大B:“如果具体策略类有一些共同的行为,则应该把它们抽取到抽象策略角色中,此时抽象策略角色为抽象类。策略模式的优点:a、提供了管理相关算法族的方法。b、可以避免使用多重条件转移语句。”
小A:“那策略模式有什么缺点?”
大B:“首先是必须知道所有的具体策略类及它们的区别,再是生成许多的策略类。”(www.xing528.com)
22.6 使用场合
大B:“一般会在哪些场合使用它?”
小A:“1、系统有许多类,而他们的区别仅仅在于它们的行为。2、动态选择几种算法中的一种。3、一个对象有很多行为。”
22.7 实现步骤
大B:“要注意策略模式的实现步骤喔。”
小A:“实现步骤?”
大B:“对啊。1、定义抽象角色类,定义好各个实现的共同抽象方法。2、定义具体策略类,具体实现父类的共同方法。3、定义环境角色类,私有化申明抽象角色变量, 重载构造方法, 执行抽象方法。”
代码:
22.8 策略模式-状态模式/模版模式的区别
小A:“状态模式和策略模式有什么不同?”
大B:“状态模式侧重状态方面,一般不会接受新的状态对象,即系统已经定义足够的状态。策略侧重不同的行为的改变在统一的接口下,强调多态下面行为的执行过程,处理过程,可以从用户那里接受参数,只要用户提供的策略符合接口。”
小A:“与模版模式又有什么不同呢?”
大B:“模版模式就是算法在父类中,子类不会完全改写算法,可以改写部分,或称关键部分,但整体的算法不变,可以节省大量代码。策略模式所有的算法均在子类中完成,强调行为即算法的不同,可以使程序更灵活。”
22.9 策略模式的应用
小A:“策略模式应该怎么去应用它?”
大B:“1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态的让一个对象在许多行为中选择一种行为。2、如果系统需要动态地在几种算法中选择一种。那么这些算法可以包装到一个个的具体算法类里面,而这些算法类都是一个抽象算法类的子类。3 、一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复发的和只与算法有关的数据。”
使用案例:
AWT中的LayoutManager,Swing中的Border.
代码例子
1、抽象策略
2、具体策略,这里我以两个具体策略为例
3、环境角色
4、客户端调用
5、执行结果:
22.10 简单的模拟鸭子应用学习
大B:“游戏中会出现各种鸭子,一边游戏戏水,一边呱呱叫,我们设计了一个鸭子超类(Duck),并让各种鸭子继承此类。”
Java代码
大B:“现在多了个主意,要让鸭子能飞,那么你可能想到只要在Duck类中加上fly()方法,然后让所有的鸭子都来继承这个方法。”
Java代码
大B:“但是,可怕的问题就出来了:“塑料橡皮鸭子”也会飞了,忽略了一件事,并非所有的鸭子都会飞。能想到继承,把橡皮鸭子类中的fly()方法覆盖掉,什么也不做。可是,如果以后要加入木头鸭子,不会飞也不会叫,你又想到了,把fly()从超类中取出来,做一个Flyable()接口,同样的方式,设计一个会叫的接口Quackable()。”
小A:“但是你没发现这么一来重复的代码会很多吗?”
大B:“现在我们知道使用继承并不能很好的解决问题,因为鸭子的行为在子类里不断的改变,让所有的子类都有这些行为是不恰当的。”
第一个设计原则:
找出应用中可能需要变化之处,把他独立出来,不要和那些不需要变化的代码混在一起。现在知道Duck()类里的fly()和quack()会随着鸭子的不同而改变,我们把它从Duck()类里取出来,写2个接口:
Java代码
这样的设计可以让这2个动作行为与鸭子类无关,可以被其他的对象复用,也可以新增加一些行为。
第二个设计原则:
针对接口编程,而不是针对实现编程。
现在来整合下鸭子的行为:
Java代码
1、Duck()抽象类
2、FlyBehavior()接口与2个行为实现类
4、来看看如何设定FlyBehavior和QuackBehavior的实例变量(继承)
5、制造一个新的鸭子类型
6、建立一个新的FlyBehavior类型,火箭动力的飞行行为
7、测试类
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。