资讯专栏INFORMATION COLUMN

Java 设计模式之策略模式

张率功 / 1881人阅读

摘要:抽象策略策略类,通常是一个接口或者抽象类。策略模式设计图改造原来的鸭子类代码实现这里我们将类定义成抽象类,并把方法定义成抽象方法。策略模式提供了可以替换继承关系的办法。使用策略模式可以避免使用多重条件转移语句。四参考资料设计模式

一、了解策略模式

1.1 什么是策略模式

策略模式 (Strategy Pattern) 是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换。此模式让算法的变化独立于使用算法的客户。

1.2 策略模式组成结构

环境 (Context):持有一个策略类的引用,最终给客户端调用。

抽象策略 (Strategy): 策略类,通常是一个接口或者抽象类。

具体策略 (ConcreteStrategy):实现了策略类中的策略方法,封装相关的算法和行为。

1.3 策略模式 UML 图解

1.4 策略模式应用场景

多个类只区别在表现行为不同,可以使用 Strategy 模式,在运行时动态选择具体要执行的行为。

需要在不同情况下使用不同的策略 (算法),或者策略还可能在未来用其它方式来实现。

对客户隐藏具体策略 (算法) 的实现细节,彼此完全独立。

二、策略模式具体应用

2.1 问题描述

模拟鸭子游戏:游戏中会出现各种鸭子,一边游泳戏水、一边呱呱叫,为了提高游戏的乐趣,加入了让鸭子飞的功能。但是考虑到并不是所有的鸭子都会飞,比如像小孩子游泳时玩的橡皮鸭。现在让你利用 OO 技术,设计鸭子相关的类。

2.2 使用继承


我们可能想到使用继承,在超类 Duck 中定义鸭子的相关方法,并实现其对应的动作,这样就能让所有鸭子都可以对应其 fly() 的动作。在定义橡皮鸭时,只需要覆盖其父类 (Duck) 中的 fly() 方法即可。

如果我们还想加入诱饵鸭,这种鸭子既不会叫,也不会飞,那么我们就要继承 Duck 类,重写其中的 quack()display()fly() 方法。

这种通过继承的方法是可以解决问题,但是有很多的局限

代码在多个子类中重复。

运行时的行为不容易改变。

很难知道鸭子的全部行为。

改变会牵一发动全身,造成其他鸭子不想要的改变。

2.3 使用接口

认识到上面继承的不足,我们可能想到了另一种方式去解决这种问题,通过接口的方式去实现某些动作。把 fly()quack() 方法从 Duck 类中抽取抽取出来,分别放在 FlyableQuackable 接口中。

通过接口的方式是可以完成任务,但是这也确实是一个很笨的方式。因为对于很多种鸭子来说,它们大部分都会飞与呱呱叫,但是我们在定义它们类的时候都要去实现 FlyableQuackable 接口,这样一来重复的代码更多了。

2.4 问题归零

到这里我们知道使用继承并不能很好的解决问题,因为鸭子的行为在子类中是不断变化的,并且让所有的鸭子都具有这些行为是不恰当的,比如橡皮鸭不具有飞的行为。通过接口的方式似乎还不错,但是 Java 接口并不具备实现代码,所以继承接口并不能达到代码复用的目的,一不小心,就可能造成新的错误!

幸运的是,有一个设计原则,恰好适用于这种状况:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。这样以来,代码变化引起的后果变少,系统将更有弹性。

2.5 策略模式登场

除了 fly()quack() 方法之外,Duck 中的其他方法还算一切正常,没有什么需要经常需要变化或修改的地方。所以除了 fly()quack() 方法,我们不打算对 Duck 中的其他方法做太多处理。我们希望一切具有弹性,正是因为没有弹性,上面两种方法都被我们淘汰掉了。

比如说,我们要产生一个绿头鸭的实例,并制定特定“类型”的飞行行为给它。我们可以在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态地“改变”绿头鸭的飞行行为。

有了这些实现目标,于是就有了第二个设计原则:针对接口编程,而不是针对实现编程。

这里我们使用接口代表每个行为,比如说,FlyBehaviorQuackBehavior,而行为的每个实现都将实现其中一个接口。所以这次鸭子类不会去实现 FlyableQuackable 接口,反而是由我们制造一组其他类专门实现 FlyBehaviorQuackBehavior,这就称为“行为”类。由行为类而不是 Duck 类来实现该接口。

(1)策略模式设计图

改造原来的鸭子类

(2) 代码实现

这里我们将 Duck 类定义成抽象类,并把 display() 方法定义成抽象方法。

接口 QuackBehavior

package com.jas.strategy;

public interface QuackBehavior {
    void quack();
}

接口 QuackBehavior 实现类 Quack(实现鸭子呱呱叫)

package com.jas.strategy;

public class Quack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Quack!");
    }
}

接口 QuackBehavior 实现类 SQuack(实现鸭子橡皮吱吱叫)

package com.jas.strategy;

public class SQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("SQuack!");
    }
}

接口 QuackBehavior 实现类 MuteQuack(实现鸭子不会叫)

package com.jas.strategy;

public class MuteQuack implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("Silence!");
    }
}

接口 FlyBehavior

package com.jas.strategy;

public interface FlyBehavior {
    void fly();
}

接口 FlyBehavior 实现类 FlyWithWings(实现鸭子飞)

package com.jas.strategy;

public class FlyWithWings implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I"m flying!");
    }
}

接口 FlyBehavior 实现类 FlyNoWay(实现鸭子不会飞)

package com.jas.strategy;

public class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("I can"t fly!");
    }
}

Duck

package com.jas.strategy;

public abstract class Duck {
    private QuackBehavior quackBehavior;
    private FlyBehavior flyBehavior;
    
    public void swim(){
        System.out.println("All ducks float.");
    }
    
    public abstract void display();
    
    public void performQuack(){
        quackBehavior.quack();
    }

    public void performFly(){
        flyBehavior.fly();
    }
    
    public void setQuackBehavior(QuackBehavior quackBehavior){
        this.quackBehavior = quackBehavior;
    }
    
    public void setFlyBehavior(FlyBehavior flyBehavior){
        this.flyBehavior = flyBehavior;
    }
    
}

测试类 RubberDuck

package com.jas.strategy;

public class RubberDuck extends Duck {

    @Override
    public void display() {
        System.out.println("Rubber Duck");
    }
    
    public static void main(String[] args) {
        
        Duck rubberDuck = new RubberDuck();    //橡皮鸭实例
        
        rubberDuck.setQuackBehavior(new SQuack());    //橡皮鸭吱吱叫
        rubberDuck.setFlyBehavior(new FlyNoWay());    //橡皮鸭不会飞
        
        rubberDuck.performQuack();
        rubberDuck.performFly();
    }
}

//输出
//SQuack!
//I can"t fly!

2.6 从策略模式组成结构对问题进行总结

三、策略模式总结

3.1 策略模式的优缺点

优点

策略模式提供了管理相关的算法族的办法,从而避免重复的代码。

策略模式提供了可以替换继承关系的办法。因为继承使得动态改变算法或行为变得不可能。

使用策略模式可以避免使用多重条件转移语句。

缺点

客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

策略模式造成很多的策略类,每个具体策略类都会产生一个新类。

四、参考资料

《Head First 设计模式》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77250.html

相关文章

  • php设计模式

    摘要:我们今天也来做一个万能遥控器设计模式适配器模式将一个类的接口转换成客户希望的另外一个接口。今天要介绍的仍然是创建型设计模式的一种建造者模式。设计模式的理论知识固然重要,但 计算机程序的思维逻辑 (54) - 剖析 Collections - 设计模式 上节我们提到,类 Collections 中大概有两类功能,第一类是对容器接口对象进行操作,第二类是返回一个容器接口对象,上节我们介绍了...

    Dionysus_go 评论0 收藏0
  • php设计模式

    摘要:我们今天也来做一个万能遥控器设计模式适配器模式将一个类的接口转换成客户希望的另外一个接口。今天要介绍的仍然是创建型设计模式的一种建造者模式。设计模式的理论知识固然重要,但 计算机程序的思维逻辑 (54) - 剖析 Collections - 设计模式 上节我们提到,类 Collections 中大概有两类功能,第一类是对容器接口对象进行操作,第二类是返回一个容器接口对象,上节我们介绍了...

    vspiders 评论0 收藏0
  • 我的Java设计模式-策略模式

    摘要:孙膑心里一万个草泥马在奔腾,差点没噎死自己滚一边去,我们这盘跟他赛马开始,策略模式上场。在设计模式之禅中的提出通过策略枚举和反射机制对策略模式进行改良,膜拜了但是要添加或淘汰策略,还是得去对枚举进行修改,也不符合开闭原则。 今天给大家说说田忌赛马的故事。如有雷同,纯属巧合!话说在战国时期,群雄割据,硝烟四起,茶余饭后还是少不了娱乐活动的,其中赛马是最火爆的。一天,孙膑看到田忌像个死鸡似...

    zilu 评论0 收藏0
  • Java设计模式策略模式详解

    摘要:下面我们就来介绍怎么用策略模式来解决这个问题。结果很快的跳很快的跑红蓝相间的超人狗不会跳不会跑红蓝相间的超人狗总结策略模式就是把所有的可变的行为都抽取出来放到接口中,然后定义很多的行为类去实现接口。 策略模式就是定义了一系列的的算法,将它们都单独封装起来,让他们之间可以相互替换,可以让算法的变化独立于使用算法的客户。 首先创建一个Dog父类,有run方法控制跑,jump方法控制跳,c...

    DevTalking 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<