设计模式-策略模式

一、定义

策略模式比较容易理解且很常见,主要思想就是将同一类型的算法封装为一个算法组,使得他们之间可以相互替换,此模式让算法变化独立于使用算法的客户。可能这样直接说比较抽象,下面的实现通过一个经典的鸭子类的例子来实现策略模式。

二、实现

面临的问题

首先有一个鸭子基类Duck,不同类型的鸭子都集成于这个基类,基类中有一些基本的方法:叫、游泳、飞,子类都会继承这些方法。

java 复制代码
public abstract class Duck {

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

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

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

    //每种鸭子外观不同,所以是抽象的
    public abstract void display();
}

public class GaDuck extends Duck{
    @Override
    public void display() {
        System.out.println("嘎嘎鸭的外观:黄头");
    }
}

public class MallardDuck extends Duck {
    @Override
    public void display() {
        System.out.println("绿头鸭的外观:绿头");
    }
}

这样没什么问题,但是如果新加了一个橡皮鸭类型呢,橡皮鸭既不会飞也不会叫,那应该怎么办?

这样也比较容易想到,重写父类的方法就好了嘛。

java 复制代码
public class RubberDuck extends Duck{

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

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

    @Override
    public void display() {
        System.out.println("这是一个橡皮鸭");
    }
}

//测试:
public class DuckTest {
    public static void main(String[] args) {
        Duck rubberDuck = new RubberDuck();
        testDuck(rubberDuck);
    }
    public static void testDuck(Duck duck){
        duck.fly();
        duck.quack();
        duck.display();
    }
}

//输出:
不会飞
不会叫
这是一个橡皮鸭

来了一个不会飞不会叫的橡皮鸭,我们可以重写父类的方法改进,但是如果后面又来了一种同样不会飞不会叫的木头鸭子呢?可能也是一样,重写方法,来多少个就重写多少个,但是这样是不是有悖于我们的设计原则?

  1. 首先,会出现大量的重复代码,都重写父类的方法改为不会飞不会叫;
  2. 然后这些行为都是重复的写死在类中,属于是直接面向实现编程而不是面向抽象编程;
  3. 最后,如果我们想修改某一类鸭子的行为,只能通过修改代码来实现,这是最不能接受的。

改进

既然飞和叫是每个鸭子都会有的行为,只不过不同类型的鸭子实现方法不同,且这些行为也不像鸭子的外观那样几乎每种鸭子都是独一无二的,比如飞的行为,只有两种,会飞和不会飞。

那我们就可以把这两种行为抽象为接口,具体的鸭子类可以自主选择这类行为下的实现。

首先,定义好行为的算法组,同时为了方便,我们直接把所有的行为都提前创建好实例放到工厂中。

飞行的行为:

java 复制代码
public interface FlyBehavior {
    void fly();
}
public class FlyWithNoWay implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("不能飞行");
    }
}
public class FlyWithWings implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("使用翅膀飞行");
    }
}
public enum FlyEnum {
    NO_WAY("noWay","无法飞"),
    WING("wing","使用翅膀飞");
    //键名
    private final String key;
    //描述
    private final String desc;
   //...
}
public class FlyFactory {
    private static final Map<String,FlyBehavior> FLY_MAP = new HashMap<>();
    static{
        FLY_MAP.put(FlyEnum.NO_WAY.getKey(), new FlyWithNoWay());
        FLY_MAP.put(FlyEnum.WING.getKey(), new FlyWithWings());
    }
    public static FlyBehavior get(String flyKey){
        FlyBehavior flyBehavior = FLY_MAP.get(flyKey);
        if(flyBehavior == null){
            throw new RuntimeException("指定的飞行方式不存在");
        }
        return flyBehavior;
    }
}

叫的行为:

java 复制代码
public interface QuackBehavior {
    void quack();
}
public class QuackWithGa implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("嘎嘎叫");
    }
}
public class QuackWithGua implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("Gua Gua 叫");
    }
}
public class QuackWithNoWay implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("不会叫");
    }
}
public enum QuackEnum {
    GUAGUA("guagua", "呱呱叫"),
    GAGA("gaga", "嘎嘎叫"),
    NO_WAY("noWay", "不会叫")
    ;
    //键值
    private final String key;
    //描述
    private final String desc;
    //...
}
public class QuackFactory {
    private static final Map<String, QuackBehavior> QUACK_MAP = new HashMap<>();
    static{
        QUACK_MAP.put(QuackEnum.GUAGUA.getKey(), new QuackWithGua());
        QUACK_MAP.put(QuackEnum.GAGA.getKey(), new QuackWithGa());
        QUACK_MAP.put(QuackEnum.NO_WAY.getKey(), new QuackWithNoWay());
    }
    public static QuackBehavior get(String quackKey){
        QuackBehavior quackBehavior = QUACK_MAP.get(quackKey);
        if(quackBehavior == null){
            throw new RuntimeException("指定的叫声方式不存在");
        }
        return quackBehavior;
    }
}

然后修改鸭子的基类,把飞和叫的行为抽象为接口,交由子类从对应的算法组中选择

java 复制代码
public abstract class Duck {
    //飞和叫会因为鸭子的不同而不同,所以使用策略模式,将他们抽离出来
    private FlyBehavior flyBehavior;
    private QuackBehavior quackBehavior;
    //抽象方法,说明
    public abstract void display();
    public void swim(){
        System.out.println("所有鸭子都会游泳");
    }
    public void performFly(){
        flyBehavior.fly();
    }
    public void performQuack(){
        quackBehavior.quack();
    }
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior = flyBehavior;
    }
    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior = quackBehavior;
    }
}
//子类,嘎嘎鸭
public class GaDuck extends Duck {
    public GaDuck() {
        setFlyBehavior(FlyFactory.get(FlyEnum.WING.getKey()));
        setQuackBehavior(QuackFactory.get(QuackEnum.GAGA.getKey()));
    }
    @Override
    public void display() {
        System.out.println("这是一个嘎鸭");
    }
}
//子类,橡皮鸭
public class RubberDuck extends Duck {
    public RubberDuck() {
        setFlyBehavior(FlyFactory.get(FlyEnum.NO_WAY.getKey()));
        setQuackBehavior(QuackFactory.get(QuackEnum.NO_WAY.getKey()));
    }
    @Override
    public void display() {
        System.out.println("这是一只橡皮鸭");
    }
}

下面就可以测试了

java 复制代码
public class DuckTest {
    public static void main(String[] args) {
        System.out.println("======================");
        testRubberDuck();
        System.out.println("======================");
        testGaDuck();
    }
    public static void testRubberDuck(){
        System.out.println("创造一个橡皮鸭,不会飞,不会叫");
        Duck rubberDuck = new RubberDuck();
        rubberDuck.display();
        rubberDuck.swim();
        rubberDuck.performFly();
        rubberDuck.performQuack();
    }

    public static void testGaDuck(){
        System.out.println("创造一个嘎嘎鸭,会飞,嘎嘎叫");
        Duck gagaDuck = new GaDuck();
        gagaDuck.display();
        gagaDuck.swim();
        gagaDuck.performFly();
        gagaDuck.performQuack();
        System.out.println("修改嘎嘎鸭,让他不会飞");
        gagaDuck.setFlyBehavior(FlyFactory.get(FlyEnum.NO_WAY.getKey()));
        gagaDuck.performFly();
    }
}

//测试结果:
======================
创造一个橡皮鸭,不会飞,不会叫
这是一只橡皮鸭
所有鸭子都会游泳
不能飞行
不会叫
======================
创造一个嘎嘎鸭,会飞,嘎嘎叫
这是一个嘎鸭
所有鸭子都会游泳
使用翅膀飞行
嘎嘎叫
修改嘎嘎鸭,让他不会飞
不能飞行

至此,我们看到通过策略模式,实现很灵活的设置鸭子的飞和叫的行为,且支持改变这些行为而不用修改代码。

相关推荐
叶甯1 小时前
【设计模式】单例模式
设计模式
诺亚凹凸曼5 小时前
23种设计模式-行为型模式-模板方法
设计模式
云徒川6 小时前
【设计模式】责任链模式
设计模式·责任链模式
振鹏Dong6 小时前
剖析Spring中的设计模式(一) | 工厂&观察者
java·spring·设计模式
user_agent7 小时前
MCP 了解一下
前端·设计模式
Koma-forever7 小时前
java设计模式-代理模式
java·设计模式·模板方法模式
hycccccch9 小时前
设计模式(23种设计模式简介)
设计模式
小胖子许愿18 小时前
设计模式 - 代理模式
设计模式·代理模式
Sitarrrr1 天前
【WPF】IOC控制反转的应用:弹窗但不互相调用ViewModel
设计模式·c#·wpf
蓝天居士1 天前
软考 系统架构设计师系列知识点 —— 设计模式之抽象工厂模式
设计模式·系统架构·抽象工厂模式