设计模式-策略模式

一、定义

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

二、实现

面临的问题

首先有一个鸭子基类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();
    }
}

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

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

相关推荐
一个处女座的程序猿O(∩_∩)O7 小时前
React 设计模式:实用指南
前端·react.js·设计模式
扫地僧0097 小时前
第17章 读写锁分离设计模式(Java高并发编程详解:多线程与系统设计)
java·前端·设计模式
C182981825757 小时前
常用设计模式
设计模式
扫地僧0097 小时前
第19章 Future设计模式(Java高并发编程详解:多线程与系统设计)
java·python·设计模式
LUCIAZZZ11 小时前
通过Demo案例的形式弄懂Java中的设计模式
java·开发语言·spring boot·spring·设计模式·代理模式
FLZJ_KL11 小时前
【设计模式】【行为型模式】策略模式(Strategy)
java·设计模式·策略模式·java基础
黑不溜秋的12 小时前
C++ 设计模式 - 访问者模式
设计模式·访问者模式
sniper_fandc14 小时前
责任链模式
java·设计模式·责任链模式
天堂的恶魔94616 小时前
C++设计模式 —— 工厂模式
javascript·c++·设计模式
天堂的恶魔94616 小时前
C++设计模式 —— 单例模式
c++·单例模式·设计模式