首页 > 其他 > 详细

策略模式

时间:2021-03-16 22:26:37      阅读:26      评论:0      收藏:0      [点我收藏+]

策略模式

策略模式简介

定义

? 策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

优点

  1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句。
  2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

缺点

  1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  2. 策略模式造成很多的策略类,增加维护难度。

问题引出

? 有一家游戏公司,正在开发一个关于鸭子的游戏,他们将鸭子共有的特征写在了一个父类中。

技术分享图片

abstract class Duck{
    public void quack() {
        System.out.println("嘎嘎");
    }

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();
}

class RubberHeadDuck extends Duck{
    @Override
    public void quack() {
        System.out.println("吱吱");
    }

    @Override
    public void display() {
        System.out.println("红头鸭子");
    }
}

class GreenHeadDuck extends Duck{

    @Override
    public void display() {
        System.out.println("绿头鸭子");
    }
}
public class Problem {
    public static void main(String[] args) {
        Duck greenHeadDuck = new GreenHeadDuck();
        greenHeadDuck.display();
        greenHeadDuck.quack();
        greenHeadDuck.swim();
    }
}

推导1

如果不会有需求变化的话,上面这样写是没有任何问题的,但此时要向鸭子的父类中添加一个飞的功能。于是程序员在Duck类中添加了fly方法。

abstract class Duck{
    public void quack() {
        System.out.println("嘎嘎");
    }

    public void swim() {
        System.out.println("游泳");
    }
    
    public void fly() {
         System.out.println("起飞");
    }

    public abstract void display();
}

技术分享图片

但这样修改之后就出现了一些问题,原本在鸭子的子类中,不能飞的鸭子也拥有了飞的属性。这是不符合常理的。此时橡皮鸭只需要重写父类飞的方法,就能看似解决问题。

class RubberHeadDuck extends Duck{
    @Override
    public void quack() {
        System.out.println("吱吱");
    }

    @Override
    public void fly() {
        System.out.println("我是假鸭子飞不了");
    }
}

但当需求不断变化时,比如再加个木头鸭子,气球鸭子等等,那么我们每加入一个种类,都需要去思考要不要重写父类的飞的方法,这样是很糟糕的。

推导2

针对上述问题,我们可以把这两个经常在子类中变化的方法,从父类中分离出来,分成两个接口Flyable,Quackable

技术分享图片

abstract class Duck {

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();
}

interface Flyable {
    void fly();
}

interface Quackable {
    void quack();
}

class RubberHeadDuck extends Duck implements Quackable {
    @Override
    public void quack() {
        System.out.println("吱吱");
    }

    @Override
    public void display() {
        System.out.println("红头鸭子");
    }
}

class GreenHeadDuck extends Duck implements Quackable, Flyable {

    @Override
    public void display() {
        System.out.println("绿头鸭子");
    }

    @Override
    public void fly() {
        System.out.println("起飞");
    }

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

// 新增木头鸭子
class WoodenWDuck extends Duck {

    @Override
    public void display() {
        System.out.println("木头鸭子");
    }
}

public class Problem3 {
    public static void main(String[] args) {
        GreenHeadDuck greenHeadDuck = new GreenHeadDuck();
        greenHeadDuck.display();
        greenHeadDuck.fly();
        greenHeadDuck.swim();
    }
}

这样做却是比以前更好了,但是以前是:每加入一个新的鸭子角色,程序猿就要判断,这个新鸭子角色是否会飞,是否会叫,不会飞的就重写飞方法,不会叫的就重写叫方法。
现在是:每加入一个新的鸭子角色,程序猿就要判断,这个新鸭子角色是否会飞,是否会叫,不会飞的就不实现Flyable可扩展接口,不会叫的就不实现Quackable接口。

此时程序员的任务量仍然很大。仍然要不断的判断新鸭子的属性。fly和quack这两个方法没有一点重用性。

策略模式

上述的几种解决方案,都存在一定的问题,此时使用策略模式,可以很好的实现方法的重用性。

解决方案

在策略模式中,具体的鸭子不用去实现具体的飞或者叫的行为,这些行为由特定的类去实现,鸭子类只需要去组合那些特定的实现类。

技术分享图片

鸭子飞的行为和叫的方式接口

// 飞的行为
interface FlyBehavior {
    void fly();
}
// 叫的行为
interface QuackBehavior {
    void quack();
}

飞和叫的具体实现形式

// 飞的具体形式
class FlyWithSwings implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("用翅膀飞");
    }
}

class FlyWithRocket implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("背着火箭飞");
    }
}

class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("不会飞");
    }
}
// 叫的具体形式
class QuackWithGaGa implements QuackBehavior {

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

class QuackWithZhi implements QuackBehavior {

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

class QuackWithSilence implements QuackBehavior {

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

技术分享图片

将飞的行为和叫的行为加入所有鸭子的父类Duck

abstract class Duck {

    protected FlyBehavior flyBehavior;

    protected QuackBehavior quackBehavior;

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();

    // 飞的获取方式
    public void performFly() {
        flyBehavior.fly();
    }

    // 叫的获取方式
    public void performQuack() {
        quackBehavior.quack();
    }

}

不同鸭子的不同表现形式,只需在不同的鸭子中去组合那些行为,而无需继承

class QuackWithZhi implements QuackBehavior {

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

class QuackWithSilence implements QuackBehavior {

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

class RubberHeadDuck extends Duck {

    public RubberHeadDuck() {
        this.quackBehavior = new QuackWithZhi();
    }

    @Override
    public void display() {
        System.out.println("红头鸭子");
    }
}

class GreenHeadDuck extends Duck {

    public GreenHeadDuck() {
        this.flyBehavior = new FlyWithSwings();
        this.quackBehavior = new QuackWithGaGa();
    }

    @Override
    public void display() {
        System.out.println("绿头鸭子");
    }

}

// 新增木头鸭子
class WoodenWDuck extends Duck {

    public WoodenWDuck() {
        this.flyBehavior = new FlyNoWay();
        this.quackBehavior = new QuackWithSilence();
    }

    @Override
    public void display() {
        System.out.println("木头鸭子");
    }
}

测试

public class StrategyPatten {
    public static void main(String[] args) {
        Duck woodenWDuck = new WoodenWDuck();
        woodenWDuck.performFly();
        woodenWDuck.performQuack();
        woodenWDuck.display();

        Duck greenHeadDuck = new GreenHeadDuck();
        greenHeadDuck.performFly();
        greenHeadDuck.performQuack();
        greenHeadDuck.display();
    }
}
不会飞
不会叫
木头鸭子
用翅膀飞
嘎嘎叫
绿头鸭子

总的代码

abstract class Duck {

    protected FlyBehavior flyBehavior;

    protected QuackBehavior quackBehavior;

    public void swim() {
        System.out.println("游泳");
    }

    public abstract void display();

    // 飞的获取方式
    public void performFly() {
        flyBehavior.fly();
    }

    // 叫的获取方式
    public void performQuack() {
        quackBehavior.quack();
    }

}

// 飞的行为
interface FlyBehavior {
    void fly();
}

// 飞的具体形式
class FlyWithSwings implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("用翅膀飞");
    }
}

class FlyWithRocket implements FlyBehavior {

    @Override
    public void fly() {
        System.out.println("背着火箭飞");
    }
}

class FlyNoWay implements FlyBehavior {
    @Override
    public void fly() {
        System.out.println("不会飞");
    }
}

// 叫的行为
interface QuackBehavior {
    void quack();
}

// 叫的具体形式
class QuackWithGaGa implements QuackBehavior {

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

class QuackWithZhi implements QuackBehavior {

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

class QuackWithSilence implements QuackBehavior {

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

class RubberHeadDuck extends Duck {

    public RubberHeadDuck() {
        this.quackBehavior = new QuackWithZhi();
    }

    @Override
    public void display() {
        System.out.println("红头鸭子");
    }
}

class GreenHeadDuck extends Duck {

    public GreenHeadDuck() {
        this.flyBehavior = new FlyWithSwings();
        this.quackBehavior = new QuackWithGaGa();
    }

    @Override
    public void display() {
        System.out.println("绿头鸭子");
    }

}

// 新增木头鸭子
class WoodenWDuck extends Duck {

    public WoodenWDuck() {
        this.flyBehavior = new FlyNoWay();
        this.quackBehavior = new QuackWithSilence();
    }

    @Override
    public void display() {
        System.out.println("木头鸭子");
    }
}

public class StrategyPatten {
    public static void main(String[] args) {
        Duck woodenWDuck = new WoodenWDuck();
        woodenWDuck.performFly();
        woodenWDuck.performQuack();
        woodenWDuck.display();

        Duck greenHeadDuck = new GreenHeadDuck();
        greenHeadDuck.performFly();
        greenHeadDuck.performQuack();
        greenHeadDuck.display();
    }
}

大致UML类图

技术分享图片

拓展

当有新的需求来的时候,比如新增了一种飞的方法,只需在Duck中添加set方法,然后再实现具体的飞的方式,最后set进Duck中就可以了。

public void setFlyBehavior(FlyBehavior flyBehavior) {
    this.flyBehavior = flyBehavior;
}

public void setQuackBehavior(QuackBehavior quackBehavior) {
    this.quackBehavior = quackBehavior;
}

应用场景

策略模式在很多地方用到,如 Java SE 中的容器布局管理就是一个典型的实例,Java SE 中的每个容器都存在多种布局供用户选择。在程序设计中,通常在以下几种情况中使用策略模式较多。

  1. 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
  2. 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
  3. 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
  4. 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
  5. 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

策略模式

原文:https://www.cnblogs.com/nuoxin/p/14546018.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!