策略(strategy)模式
Head First一书中对于策略(strategy)模式的正式定义是:策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
为了介绍这个算法,书中讲了一个例子:
在某个游戏中,有各种各样的鸭子,系统的内部设计使用了严格的OO技术,设计了一个鸭子(Duck)父类,所有的鸭子种类均继承于此父类。Joe设计的鸭子类如下:
package com.first.strategy;
public class Duck1 {
public Duck1()
{
}
//鸭子会游泳
public void swim()
{
}
//鸭子会呱呱叫
public void quack()
{
}
//鸭子会飞
public void fly()
{
}
//描述不同的鸭子,子类继承时覆盖重写
public void display()
{
}
}
然后让各种鸭子都继承此类,并且重写display()方法,例如:
class DuckA extends Duck1
{
public void display()
{
System.out.println("I am DuckA!");
}
}
这样的设计缺点显而易见:新加入的鸭子种类“橡皮鸭子”也能飞,且也能呱呱叫(橡皮鸭子不能飞也不能呱呱叫),这是违背现实的。因为所有的子类均会继承来自父类的fly()方法和quack()方法,所有的子类均具备了fly()和quack(),使得不适合子类的行为也继承了父类的行为。
这时,Joe又想到了继承,可以在子类橡皮鸭(RubberDuck)中覆盖父类的fly()和quack()方法,让其不能飞也不能呱呱叫。
package com.first.strategy;
public class RubberDuck extends Duck1{
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("我不会呱呱叫,我会吱吱叫!");
}
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I can not fly!");
}
}
这时缺点同样清楚:代码重复太多,每个子类全部要覆盖,代码的可重用性太低。
Joe想到了接口,即把Duck类中的fly和quack从父类中抽象出来,变成接口Flyable和Quackable,这样,只有会飞的鸭子子类才继承Flyable接口,只有会呱呱叫的鸭子继承Quackable接口。这也不是一个好主意,因为它的缺点同样显而易见:和上面重写覆盖类似,重复的代码太多,不易维护,要修改,子类全要修改。
既然继承并不能很好的解决这个鸭子问题,那么我们便寻求变化。鸭子的行为在子类中不断变化,并且让所有的鸭子都具有这些行为是不恰当的。Flyable和Quackable接口的想法开始不错,但是,Java接口中的方法不具有实现,所有子类继承接口无法达到代码复用的效果。这就意味着,无论何时要修改某个行为,那么必须向下追踪所有定了这个行为的类(或子类)中去修改。这时一个设计原则应运而生,恰好解决此问题:
设计原则1:找出应用中可能需要变化之处,把他们独立出来,不要把需要变化的代码和不变的代码混杂在一起。即:把会变化的的部分取出来封装起来,以便以后可以轻易的改动或扩充此部分,而不影响不会变化的部分。
现在就把变化的部分从Duck类中分离,变化的部分就是fly()和quack(),因为这两者会随着鸭子的不同而改变。我们分离出来的两个方法建立两个类,一个是与fly相关,一个与quack相关,每一组类实现各自的动作或行为。例如一个类实现“呱呱叫”,另外一个类实现“吱吱叫”,还有一个类实现“安静不叫”(例如木头鸭子)。这是运用了第二条原则:
设计原则2:针对接口编程,而不是实现。
所有的和fly相关的行为,组装成一个接口,quack接口类似。并且分别定义各种与之相关的行为。
package com.first.strategy;
public interface FlyBehavior {
public void fly();
}
package com.first.strategy;
public interface QuackBehavior {
public void quack();
}
呱呱叫类:
package com.first.strategy;
public class Quack implements QuackBehavior{
@Override
public void quack() {
// TODO Auto-generated method stub
//呱呱
System.out.println("quack..quack");
}
}
吱吱叫类:
package com.first.strategy;
public class Squeak implements QuackBehavior{
public void quack() {
// TODO Auto-generated method stub
//吱吱
System.out.println("squeak..squeak");
}
}
和fly相关的两个类:
<pre class="java" name="code"><pre class="java" name="code">package com.first.strategy;
public class FlyNoWay implements FlyBehavior{
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("Can not fly!!");
}
}
package com.first.strategy;
public class FlyWithWings implements FlyBehavior{
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("Flying with wings!");
}
}
这样一来,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为被抽象出来,与鸭子类无关了。我们可以增加一些动作,既不会影响现有的行为类,也不会影响使用这些行为类的鸭子类。
现在要整合鸭子的行为:
1.在Duck中加入两个实例变量,分别是FlyBehavior的实例变量flybehavior和QuackBehavior的实例变量quackbehavior,为借口类型而不是具体的实现类型(为了使用多态)。同时将原Duck类中的fly()和quack()方法删除,加入两个相似的方法performFly()和performQuack()
package com.first.strategy;
public abstract class Duck {
FlyBehavior flybehavior;
QuackBehavior quackbehavior;
public Duck()
{
}
public abstract void display();
public void performFly()
{
flybehavior.fly();
}
public void performQuack()
{
quackbehavior.quack();
}
public void swim()
{
System.out.println("All ducks can swim!");
}
}
2.Duck子类中对flybehavior和quackbehavior实例变量的设置:
package com.first.strategy;
public class MallardDuck extends Duck{
public MallardDuck()
{
flybehavior = new FlyWithWings();
quackbehavior = new Squeak();
}
public void display()
{
System.out.println("I am a mallardDuck!");
}
}
package com.first.strategy;
public class RubberDuck extends Duck1{
@Override
public void quack() {
// TODO Auto-generated method stub
System.out.println("我不会呱呱叫,我会吱吱叫!");
}
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I can not fly!");
}
}
3.主测试用例
package com.first.strategy;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Duck mallardduck = new MallardDuck();
mallardduck.display();
mallardduck.performFly();
mallardduck.performQuack();
}
}
4.运行程序。
动态设定行为
1.在鸭子里很多动态的功能没有用到,很可惜,我们可以在Duck类中增加setter方法来设置鸭子的行为,而不是在构造器中实例化:
package com.first.strategy;
public abstract class Duck {
FlyBehavior flybehavior;
QuackBehavior quackbehavior;
public Duck()
{
}
public void setFlyBehavior(FlyBehavior flybehavior)
{
this.flybehavior = flybehavior;
}
public void setQuackBehavior(QuackBehavior quackbehavior)
{
this.quackbehavior = quackbehavior;
}
public abstract void display();
public void performFly()
{
flybehavior.fly();
}
public void performQuack()
{
quackbehavior.quack();
}
public void swim()
{
System.out.println("All ducks can swim!");
}
}
2.构造一个新的鸭子模型:模型鸭子(ModelDuck),这个鸭子开始不会飞。
package com.first.strategy;
public class ModelDuck extends Duck{
public MallardDuck()
{
flybehavior = new FlyNoWay();//不会飞
quackbehavior = new Quack();
}
public void display()
{
System.out.println("I am a ModelDuck!");
}
}
3.建立新的FlyBehavior类型
package com.first.strategy;
public class FlyRocketPowered implements FlyBehavior{
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I can fly with a rocket!");
}
}
4.主测试类:
package com.first.strategy;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Duck modelduck = new ModelDuck();
modelduck.performFly();//开始不会飞
modelduck.setFlyBehavior(new FlyRocketPowered());
modelduck.performFly();//现在会飞了
}
}
鸭子和FlyBehavior和QuackBehavior是“HAS—A”关系,两个类组合起来(composition),鸭子的行为不是继承来的,而是组合而来。这里是第三个设计原则:
原则3:多用组合,少用继承。
使用组合组建系统具有很大的弹性,不仅可将算法族封装成类,更可以在运行时动态的改变行为。
策略(strategy)模式的更多相关文章
- Java 实现策略(Strategy)模式
策略模式:行为型模式 将同一行为,不同的处理算法分别封装起来.让它们之间能够互相替换 1. 定义一个超类型接口,及 行为方法 2. 定义不同的实现类,实现该行为的 不同的算法 /** * 策略模式:针 ...
- 设计模式C++实现(1)——策略(Strategy)模式
目录 策略模式 应用案例 实现的关键 Talk is cheap,let's See The Code 设计思想 参考 策略模式 策略模式定义了一系列算法和行为(也就是策略),他们可以在运行时相互替换 ...
- C++设计模式实现--策略(Strategy)模式
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/L_Andy/article/details/30489331 一. 举例说明 曾经做了一个程序,程序 ...
- 设计模式C++描述----15.策略(Strategy)模式
一. 举例说明 以前做了一个程序,程序的功能是评价几种加密算法时间,程序的使用操作不怎么变,变的是选用各种算法. 结构如下: Algorithm:抽象类,提供算法的公共接口. RSA_Algorith ...
- 《Head First 设计模式》ch.1 策略(Strategy)模式
策略模式 定义了算法族,分别封装起来,让它们可以互相替换,让算法的变化独立于使用算法的客户. 模式名词的意义 威力强大,交流的不止是模式名称,而是一整套模式背后所象征的质量.特性.约束 用更少的词汇做 ...
- Head First 设计模式 - 01. 策略 (Strategy) 模式
当涉及到"维护"时,为了"复用"目的而使用继承,结局并不完美 P4 对父类代码进行修改时,影响层面可能会很大 思考题 利用继承来提供 Duck 的行为,这会导致 ...
- 模板模式与策略模式/template模式与strategy模式/行为型模式
模板模式 模版模式,又被称为模版方法模式,它可以将工作流程进行封装,并且对外提供了个性化的控制,但主流程外界不能修改,也就是说,模版方法模式中,将工作的主体架构规定好,具体类可以根据自己的需要,各自去 ...
- JAVA设计模式--strategy(策略者模式)
概念策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它的客户而独立变化.(原文:The Strategy Pattern defines a fa ...
- Java策略模式(Strategy模式) 之体验
<JAVA与模式>之策略模式 在阎宏博士的<JAVA与模式>一书中开头是这样描述策略(Strategy)模式的: 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法 ...
随机推荐
- django 将model转换为字典
from django.forms.models import model_to_dict from projects.models import ProjectInformation site = ...
- linux查看匹配内容的前后几行(转)
linux系统中,利用grep打印匹配的上下几行 如果在只是想匹配模式的上下几行,grep可以实现. $grep -5 'parttern' inputfile //打印匹配行的前后5行 ...
- 16C554在LINUX上的移植(AT91)
16C554在LINUX上的移植(AT91) linux版本:3.14.17 AT91SAMa5d36 EINTA_0 ARM-IO5 PA14 14 EINTA ...
- Spider Studio 新版本 (x-mas) - 可以引入第三方程序集, 可以将脚本生成为DLL
Merry X'mas! Spider Studio本年度最后一次重大更新发生在圣诞节, 又是一次美好的巧合 :) 本次更新主要包含两个重要功能: 1. 引入第三方程序集 在"设置" ...
- MySQL5.0、5.1、5.5、5.6功能进化
目前线上使用的版本情况:新上线端口统一使用5.5,不说别的,一个快速恢复重启就值回票价. 但因为历史原因还有大量5.1的版本,甚至,I’am sorry,还有少数5.0的版本. 至于5.0以前的版本, ...
- vim 编辑器使用技巧
看着李立鹏熟练的使用vim,哥心里痒痒的,也来试试! vim pkf.txt i 插入编辑 esc -> :x //退出保存 f8 ->//进入多窗口模式 以下是其他命令: 命令历史 以: ...
- Android SDK代理server解决国内不能更新下载问题
读者须知:本篇文章中最靠谱的是第三种方式,近期有读者反映第三种方式也不行了,以下提供一点其它途径的开源镜像网站: 国内高校的开源镜像站 中国科学技术大学(debian.ustc.edu.cn) 上海交 ...
- 通过公网IP主机建立ssh隧道
环境描述 hostA: 有公网IP的linux主机 hostB: 私有路由器后端无公网IPlinux主机,能够ssh连接到hostA hostC: 个人pc机 隧道创建步骤 step1 在hostB上 ...
- 【OpenWRT】网络配置
cd /etc/config vim network vim wireless cd /etc/init.d/network
- SQL Server计算列
计算列由可以使用同一表中的其他列的表达式计算得来.表达式可以是非计算列的列名.常量.函数,也可以是用一个或多个运算符连接的上述元素的任意组合.表达式不能为子查询. 例如,在 AdventureWork ...