设计模式——(1)策略模式

为什么要学设计模式?

因为设计模式是面向对象的具体实现,你将学习其他开发人员的经验,解决在特定场景下的问题,设计模式在开发过程中随处可见,它真的很重要!!!
开始学习策略模式…

1.1 引言

在开发过程中,当遇到如下场景:

  • 一款游戏,不同的鸭子具有不同的飞行行为,例如,一般的鸭子具有飞行的行为,橡皮鸭和模型鸭不具有飞行的行为。
  • 购物时,不同会员等级对应的商品价格折扣是不同的,折扣具有不同的算法,例如,金牌会员打7折,银牌会员打8折;

就上述场景而言,针对某一问题,具有不同的应对策略(行为或算法等,统称策略)。

鸭子的飞行策略可能根据需求而变化,例如,添加一种火箭鸭,其飞行由火箭动力驱动。

如果不对这些变化的策略进行抽象和封装,可能引起代码无法复用、可维护性差和可扩展性差等问题。

在这种场景下,可以应用策略模式,来解决上述问题。

1.2 利用继承和接口添加鸭子飞行行为

一款关于鸭子的游戏,鸭子Duck是抽象类,它有很多不同类型鸭子的实现。

现在需要为鸭子加入飞行的功能,即fly(),并且不同的鸭子具有不同的飞行行为。

一、利用继承的方法

设计方案如类图1所示:
在这里插入图片描述
类图1.利用继承为鸭子添加飞行行为

Duck是抽象父类,其具有飞行行为fly(),但其子类RubberDuck不具有飞行行为,

因此,在子类RubberDuck()中,重写fly(),覆盖父类的fly()方法,使其成为一个空方法,不具有飞行行为。

如果新增一只诱饵鸭DecoyDuck,也要重写父类的fly()方法,使其不具有飞行行为,

这使得fly()空方法在子类无法复用,每增加一个不具有飞行行为的鸭子,子类均要重写fly()空方法。

试想,如果子类的fly()空方法,具有大量代码,这就会使得更多的代码无法复用。

二、利用接口的方法

设计方案如类图2所示:
在这里插入图片描述
类图2.利用接口为鸭子添加飞行行为

把fly()从超类中取出来,放进一个“Flyable接口”中。这么一来,只有会飞的鸭子才实现此接口。

根据不同的鸭子,实现自己的飞行行为。

但这样也使得,每增加一类具有飞行行为的鸭子时,其飞行行为也无法复用,例如,绿头鸭和红头鸭的飞行行为是相同的,但代码无法复用。

1.3 利用策略模式添加鸭子飞行行为

设计方案如类图3所示:
在这里插入图片描述

类图3.利用策略模式添加鸭子行为.puml

鸭子的飞行行为是变化的部分,因此,将飞行行为封装起来,通过Duck与FlyBehavior的组合,实现鸭子的飞行行为与其他代码解耦;以后若有新的飞行行为,可通过实现FlyBehavior接口添加飞行行为即可,这具有较好的扩展性,也有利于维护代码。

通过策略模式实现的代码如下:

Duck类:

package headfirst.designpatterns.strategy;

public abstract class Duck {
    FlyBehavior flyBehavior;

    public Duck() {
    }

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

    abstract void display();

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

MallardDuck类:

package headfirst.designpatterns.strategy;

public class MallardDuck extends Duck{
    @Override
    void display() {
        System.out.println("I'm a real Mallard duck");
    }
}

ModelDuck类:

package headfirst.designpatterns.strategy;

public class ModelDuck extends Duck{
    @Override
    void display() {
        System.out.println("I'm a model duck");
    }
}

RubberDuck类:

package headfirst.designpatterns.strategy;

public class RubberDuck extends Duck{
    @Override
    void display() {
        System.out.println("I'm a rubber duck");
    }
}

FlyBehavior 接口: 封装飞行类

package headfirst.designpatterns.strategy;

public interface FlyBehavior {
    void fly();
}

FlyWithWings 类:具体的飞行行为-可以飞

package headfirst.designpatterns.strategy;

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

FlyNoWay 类:具体的飞行行为-不能飞

package headfirst.designpatterns.strategy;

public class FlyNoWay implements FlyBehavior {

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

FlyRocketPowered类:新增一种火箭驱动的飞行行为,利用策略模式的可扩展性好,也便于维护。

package headfirst.designpatterns.strategy;

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

StrategyPatternDemo 测试类:

package headfirst.designpatterns.strategy;

/**
 * @author: sjmp1573
 * @date: 2022/4/29 10:59
 * @description:
 */

public class StrategyPatternDemo {

    public static void main(String[] args) {
        FlyBehavior flyNoWay = new FlyNoWay();
        Duck modelDuck = new ModelDuck();
        // 为模型鸭注入飞行行为,即无法飞行;
        modelDuck.setFlyBehavior(flyNoWay);
        modelDuck.display();
        modelDuck.performFly();
        System.out.println("------------");

        //运行时,动态改变模型鸭的飞行行为,利用火箭驱动飞行
        FlyRocketPowered flyRocketPowered = new FlyRocketPowered();
        modelDuck.setFlyBehavior(flyRocketPowered);
        modelDuck.display();
        modelDuck.performFly();
        System.out.println("------------");

        // 创建一只橡皮鸭,复用方法
        Duck rubberDuck = new RubberDuck();
        rubberDuck.setFlyBehavior(flyNoWay);
        rubberDuck.display();
        rubberDuck.performFly();
    }
}

输出:

I'm a model duck
I can't fly
------------
I'm a model duck
I'm flying with a rocket
------------
I'm a rubber duck
I can't fly

Process finished with exit code 0

结果分析:模型鸭刚开始不具有飞行行为,通过策略模式,在运行时可改变模型鸭的飞行行为,即火箭驱动。并且橡皮鸭也不具有飞行行为,复用了 FlyBehavior 接口下的 FlyNoWay 飞行行为。

以前鸭子类继承超类或直接实现接口,重写行为方法,子类依靠实现。现在通过策略模式,实现了面向接口编程。

1.4 总结

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

策略模式的 UML 图

好处在于:代码复用、扩展灵活、可维护性好。

OO原则:

  • 封装变化,(封装鸭子的飞行行为)
  • 多用组合,少用继承,(Duck与FlyBehavior的组合,而不是使用类或接口的继承)
  • 针对接口编程,不针对实现编程,(飞行的具体行为在FlyBehavior接口中实现)

GitHub 代码地址:https://github.com/SJMP1573/DesignPatterns.git

参考

[1] Freeman E. Head First 设计模式[M] 中国电力出版社.
[2] 策略模式 | 菜鸟教程.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值