1      模式简介

  在策略模式中,一个类的行为或其算法可以在运行时改变。策略模式定义了一系列算法,把它们一个个封装起来,并且使它们可以互相替换。

策略模式的优点:

  • 算法可以自由切换;
  • 避免使用多重条件判断;
  • 扩展性良好。

策略模式的缺点:

  • 算法可以自由切换;
  • 避免使用多重条件判断;
  • 扩展性良好。

策略模式的适用场景:

  • 当一个系统中有许多类,它们之间的区别仅在于它们的行为,希望动态地让一个对象在许多行为中选择一种行为时;
  • 当一个系统需要动态地在几种算法中选择一种时;
  • 当一个对象有很多的行为,不想使用多重的条件选择语句来选择使用哪个行为时。

2      案例

  在这个例子中,我们来模拟一个游戏:这个游戏中有各种鸭子,它们会飞,也会叫,但它们飞和叫的方式不同,如红头鸭是呱呱叫的,而橡皮鸭是吱吱叫的;红头鸭是用翅膀飞的,而橡皮鸭是不会飞的。我们希望用JAVA编程语言结合策略模式来完成这个功能。

2.1 使用继承

  鸭子虽然有不同的种类,但是也有一定的相同之处,所以我们可以从鸭子中提取一个父类Duck,让不同种类的鸭子类继承自父类,将所有鸭子共有的属性和行为放到父类中。这种思想对应的类图如下:

  这样做看似没有错误,但实际上存在以下两个方面的错误:

  1、子类的可用行为可能远大于我们的期待范围。把所有方法和属性都定义在Duck父类中,再由所有子类去继承这个父类,那么所有的子类就都具备父类的方法和属性。就拿这个游戏的例子来说,如果加入一个方法fly(),那么就算是橡皮鸭(RubberDuck)类也一定会具备fly()的行为。

  2、这种方法并不能有效的解决代码重复问题。拿上一条中的fly()方法来说,既然不同的鸭子具有不同的飞行方式,则fly()方法的方法体一定是写在子类中;而不同种类的鸭子的飞行方式也可能是相同的,所以我们就可能不得不在多个鸭子中使用相同的fly()方法,而在另外一些鸭子中使用另一种fly()方法。

2.2 增加接口

  我们可以把Duck父类中的fly()方法提取出来定义成接口,称为Flyable,其中定义一个抽象方法fly(),让可以飞的Duck子类实现这个接口并实现fly()方法。各个类中的行为细节及类间关系如下图所示。

  使用接口有效的解决了上面的第一个问题,即可以有效的控制各个子类的行为范围,不会出现“子类具有父类的所有方法”的情况了。但是,JAVA中的接口中不具备代码实现功能,所以这样做并没有解决代码重复的问题。

2.3 解决问题

  我们需要找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起,即把会变化的部分取出并封装起来,一边以后可以轻易的改动或扩展此部分,而不影响不需要变化的部分。

  在上面的两次尝试(使用继承、增加接口)中,我们都只是把共有的属性和行为抽出来封装成父类或接口,但在这里,我们要尽量两次封装,即在业务类上面还有两层封装类。对于一种行为,我们首先将这种行为抽取出来作为一个总的行为接口,然后再在这个接口下面定义多个不同的实现类,最后再在业务类中调用父类完成业务。简单的说,在上面两种尝试中,我们只是定义了两个互不相关的行为,而在这种方法中,我们定义的是一组有关联的行为。

  拿我们这个例子中的fly()方法举例,飞,可能是用翅膀飞(FlyWithWings),也可能是坐火箭飞(FlyRocketPower)。因此,我们定义一个总的接口FlyBehavior,在这个接口中定义一个抽象方法fly()。再定义两个实现FlyBehavior接口的类FlyWithWings和FlyRocketPower,这两个类实现FlyBehavior接口,并实现接口中的fly()方法。另外,我们在Duck父类中定义一个FlyBehavior的接口变量,在生成鸭子实体的时候,给其FlyBehavior赋值,以此决定鸭子的飞行方式。我们还可以在Duck父类中定义一个设置飞行方式的方法setFlyBehavior(),来动态的改变鸭子的飞行方式。以下是类图:

  以下贴出对这个问题的解决方案的代码:

  飞行接口FlyBehavior中的代码:

public interface FlyBehavior {
// 飞行的抽象方法
void fly();
}

  使用翅膀飞行类FlyWithWings中的代码:

public class FlyWithWings implements FlyBehavior {

    @Override
public void fly() {
System.out.println("使用翅膀飞行");
}
}
  其实还有不会飞的类FlyNoWay,这里就不贴出来了。

  叫的接口QuackBehavior中的代码:

public interface QuackBehaviro {
// 叫的抽象方法
void quack();
}

  呱呱叫的类Quack中的代码:

public class Quack implements QuackBehaviro {

    @Override
public void quack() {
System.out.println("呱呱叫");
}
}

  其实还有吱吱叫的类Squeak,这里就不贴出来了。

  鸭子的父类Duck中的代码:
public class Duck {
protected FlyBehavior flyBehavior;
protected QuackBehaviro quackBehaviro; public void performFly() {
flyBehavior.fly();
} public void performQuack() {
quackBehaviro.quack();
} public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
} public void setQuackBehaviro(QuackBehaviro quackBehaviro) {
this.quackBehaviro = quackBehaviro;
}
}

  鸭子的一个子类红头鸭RedHeadDuck中的代码:

public class RedHeadDuck extends Duck {

    public RedHeadDuck() {
System.out.println("只是一只红头鸭");
super.flyBehavior = new FlyWithWings();
super.quackBehaviro = new Quack();
}
}

  鸭子的另一个子类橡皮呀RubberDuck中的代码:

public class RubberDuck extends Duck {

    public RubberDuck() {
System.out.println("这是一只橡皮鸭");
super.flyBehavior = new FlyNoWay();
super.quackBehaviro = new Squeak();
}
}

  测试类Test中的代码:

public class Test {
public static void main(String[] args) {
Duck redHeadDuck = new RedHeadDuck();
redHeadDuck.performFly();
redHeadDuck.performQuack(); System.out.println(); Duck rubberDuck = new RubberDuck();
rubberDuck.performFly();
rubberDuck.performQuack();
}
}

  运行结果如下图所示:

  下面贴出策略模式代码的GitHub地址:【GitHub - Strategy】

【设计模式 - 22】之策略模式(Strategy)的更多相关文章

  1. 【转】设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

    设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成 ...

  2. 设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

    设计模式 ( 十八 ) 策略模式Strategy(对象行为型) 1.概述 在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕 ...

  3. JavaScript设计模式 Item 7 --策略模式Strategy

    1.策略模式的定义 何为策略?比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路. 如果没有时间但是不在乎钱,可以选择坐飞机. 如果没有钱,可以选择坐大巴或者火车. 如果再穷一点,可以选 ...

  4. 设计模式系列之策略模式(Strategy Pattern)

    意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换. 主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护. 何时使用:一个系统有许多许多类,而区分它 ...

  5. Java描述设计模式(22):策略模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 每年双十一,各大电商平台会推出不同的满减策略,当用户的消费金额满一定额度后,会进行减去一定的优惠额度,从而来一波清仓甩卖,使用策 ...

  6. 设计模式 行为型 - 策略模式 Strategy

    策略模式(Strategy) 意图 对象有某个行为,但是在 不同的场景 下,该行为有 不同的实现算法. 就好比你去餐馆吃饭,首页你要通过菜单来选择你想吃的菜,根据你点的菜的不同,在厨房中去做不同的菜. ...

  7. 《JAVA设计模式》之策略模式(Strategy)

    在阎宏博士的<JAVA与模式>一书中开头是这样描述策略(Strategy)模式的: 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它 ...

  8. 《Head First 设计模式》[01] 策略模式

    <Head First 设计模式>(点击查看详情) 1.写在前面的话 之前在列书单的时候,看网友对于设计模式的推荐里说,设计模式的书类别都大同小异,于是自己就选择了Head First系列 ...

  9. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  10. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

随机推荐

  1. php提取背景图片

    preg_match_all('/background\s*-\s*+image\s*:\s*url\s*\("*([^"]*)"*\)/i', $content,$ma ...

  2. odoo 的 拉式 和 推式 库链

    推式链的数据定义在  stock.location.path 表,视图定义在 “路线” 界面的 “push rules” 具体可参考  入库设置为  Receipt in 2 steps . push ...

  3. 在ajax当中使用url重写来避免url的暴露

    记得一次面试,有这样一道面试题:jsp页面当中需要用到ajax的实现,此时需要调用java的url:此时的问题是如果用户查看页面源码就能看到真是的url,这个问题如何避免.说实话,AJAX我用的只是皮 ...

  4. Python的数据类型总结

    原地可变类型和不可变类型 原地不可变类型又叫可哈希(hashable)类型,原地可变类型又叫不可哈希类型. 原地不可变类型: 数字类型:int, float, decimal.Decimal, fra ...

  5. 工作总结:文件对话框的分类(C++)

    原文地址:http://www.jizhuomi.com/software/173.html 文件对话框分为打开文件对话框和保存文件对话框,相信大家在Windows系统中经常见到这两种文件对话框.例如 ...

  6. python JSON处理

    概念 序列化(Serialization):将对象的状态信息转换为可以存储或可以通过网络传输的过程,传输的格式可以是JSON.XML等. 反序列化:就是从存储区域(JSON,XML)读取反序列化对象的 ...

  7. MIME

    http://www1.huachu.com.cn/read/readbookinfo.asp?sectionid=1000000558 http://www.jb51.net/hack/10623. ...

  8. DJANGO,获取当前用户名,用户组名,用户组权限

    样例,为下一步自定义用户权限作一下代码准备: def get_context_data(self, **kwargs): if self.request.user.is_authenticated() ...

  9. Dynamips/Dynagen模拟CISCO路由环境

    今天将<网络互连技术>--路由,交换与远程访问实训教程的实验书拿出来了看了部门. 搭建了一个基于DYNAGEN的虚拟环境. 归纳一下大约步骤: ~~~~~~~~~~~~~~ 一,在WIND ...

  10. Qt无边框MainWindow如何拖动四周改变大小

    原来还有winEvent(), x11Event() and macEvent() 这些东西...不过貌似还需要找更好的办法,否则就无法跨平台了. 你需要重新处理部分窗体事件,以下代码适用于Windo ...