设计模式之策略模式

本文通过一个生产鸭子程序的例子,详细介绍了策略模式的概念及其应用。对比了继承和接口方式的不足,展示了策略模式如何通过将算法封装成独立的对象,使得算法可以自由切换,易于扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

《Head First》中对策略模式定义如下:策略模式定义了算法族,分别封装起来,让他们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。

ok,相信对于初学者而言,看到这个定义肯定一脸懵逼,没关系,往下看,我会用实际例子让你明白什么是策略模式。

案例

背景

现在要编写一个不断生产鸭子的程序,这些鸭子其他地方都一样,只有飞行行为(flyBehavior)和叫的行为(quackBehavior)不同。

方案一:继承

思路:使用一个超类Duck统领全局,其他鸭子类继承Duck类

/**
 * 鸭子超类
 *
 * @author wb-ny291824
 * @version $Id: Duck.java, v 0.1 2018-03-16 16:56 wb-ny291824 Exp $$
 */
public abstract class Duck {

    /**
     * 腿的数量
     */
    protected int legs;

    /**
     * 飞行
     */
    public void fly() {
        System.out.println("I can fly");
    }

    /**
     * 叫
     */
    public void quack() {
        System.out.println("呱呱呱");
    }
}

然后编写一个子类,继承Duck类,为了让不同的鸭子有不同的行为,重写fly()和quack()方法

/**
 * 野鸭
 * @author wb-ny291824
 * @version $Id: Duck2.java, v 0.1 2018-03-16 17:04 wb-ny291824 Exp $$
 */
public class MallardDuck extends Duck{

    @Override
    public void fly() {
        System.out.println("I can not fly");
    }

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

如果有更多的鸭子,则创建更多的xxxDuck类。

这种方式的问题很明显:

  • Duck类的代码会在子类重复
  • 一旦Duck类的代码改变,所有子类代码都会变
  • 需要不断的覆盖fly()和quack()方法,即使有的鸭子不会飞也不会叫

方案二:使用接口

针对方案一的问题,我们将fly()和quack()方法抽取出来,分别单独放在Flyable和Quackable接口。如果一个鸭子只会飞,不会叫,那么就只实现Flyable()接口就可以了,不需要实现Quackable()接口。
Uml图如下:
这里写图片描述

这种方式看起来不错,解决了在继承中不会叫的鸭子却要实现quack()的尴尬,但是依旧解决不了代码重复使用的问题。这意味着:
无论何时你需要修改某个行为,你必须得往下追踪,并在每一个定义此行为的类中修改它,一不小心,可能造成新的错误。
换句话说,如果每次心得需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分的代码需要被抽取出来,和稳定的代码有所区分。所以我们在设计程序的时候,切记:

把会变化的部分取出来封装起来,以便以后可以在不改变其他稳定部分的情况下,轻易的改动或扩展此部分。

方案三:使用策略模式

在鸭子类中,经常变化的是什么呢?没错,就是fly()和quack()。所以接下来我们要做的就是将这两部分经常变化的行为抽取出来。

public interface FlyBehavior {
    public void fly();
}
public interface QuackBehavior {
    public void quack();
}

接下来是上面两个接口的实现类:

public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("I'm flying!!");
    }
}
public class MuteQuack implements QuackBehavior {
    public void quack() {
        System.out.println("<< Silence >>");
    }
}

Uml图如下:
这里写图片描述

接下来,只需要在Duck类中定义FlyBehavior和QuackHehavior的成员变量即可。


public abstract class Duck {
    //飞行行为
    FlyBehavior flyBehavior;
    //叫
    QuackBehavior quackBehavior;

    public Duck() {
    }

    public void setFlyBehavior (FlyBehavior fb) {
        flyBehavior = fb;
    }

    public void setQuackBehavior(QuackBehavior qb) {
        quackBehavior = qb;
    }

    abstract void display();

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }
}

那么,对于不同的鸭子而言:

public class MallardDuck extends Duck {

    public MallardDuck() {

        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();


    }

    public void display() {
        System.out.println("I'm a real Mallard duck");
    }
}
public class ModelDuck extends Duck {
    public ModelDuck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Quack();
    }

    public void display() {
        System.out.println("I'm a model duck");
    }
}

MallardDuck和ModelDuck的飞行行为是不一样的。
在这个方案中,我们关心的只是Duck “has a”飞行行为和叫的行为,强调的是拥有,而不关心具体的实现。

如果我想加一个靠火箭飞行的鸭子怎么做呢?
首先增加一个火箭飞行的实现类:

public class FlyRocketPowered implements FlyBehavior {
    public void fly() {
        System.out.println("I'm flying with a rocket");
    }
}

然后增加火箭鸭

public class RocktDuck extends Duck {
    public RocktDuck() {
        setFlyBehavior(new FlyRocketPowered());
        setQuackBehavior(new MuteQuack());
    }
    public void display() {
        System.out.println("I'm a RocktDuck");
    }
}

现在,我们回过头来,再看看策略模式的定义:

策略模式定义了算法族,分别封装起来,让他们之间可以互相替换。此模式让算法的变化独立于使用算法的客户。

策略模式定义了算法族,这里的算法族就是FlyBehavior和QuackBehavior以及他们的实现类。这一系列的算法,你可以随意的替换,想用哪一个就用哪一个,想怎么组合就怎么组合。它们的组合变化不会影响到Duck的其他属性。

策略模式的优缺点

优点

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

缺点

  • 策略类会增多。
  • 所有策略类都需要对外暴露。

使用场景

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  • 一个系统需要动态地在几种算法中选择一种。
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

注意事项

如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值