专栏首页spring源码深度学习策略模式——运筹帷幄

策略模式——运筹帷幄

一、定义

定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。

二、抛砖引玉

这个通俗易懂的小栗子,原版出自《设计模式之禅(第二版)》。

三国情景再现: 诸葛亮在刘备去东吴招亲之前,特授予伴郎赵云三个锦囊,说是按天机拆开解决棘手问题。

这三个妙计分别是:找乔国老帮忙(也就是走后门了),求吴国太放行(开绿灯)以及孙夫人断后。

接下来我们模拟这三个妙计的使用场景。

//妙计接口:
public interface Strategy {
    void operate();
}


//妙计具体实现(一)——乔国老开后门
public class BackDoor implements Strategy {
    @Override
    public void operate() {
        System.out.println("找乔国老帮忙,让吴国太给孙权施加压力");
    }
}
//妙计具体实现(二)——求吴国太放行(开绿灯)
public class GivenGreenLight implements Strategy {
    @Override
    public void operate() {
        System.out.println("求吴国太开绿灯,放行");
    }
}

//妙计具体实现(三)——孙夫人断后
public class BlockEnemy implements Strategy {
    @Override
    public void operate() {
        System.out.println("孙夫人断后,挡住追兵");
    }
}

//装锦囊的袋子
public class Context {
    private Strategy strategy;
    public Context(Strategy strategy){
        this.strategy = strategy;
    }

    //使用计谋
    public void operate(){
        this.strategy.operate();
    }
}

//赵云
public class ZhaoYun {
    public static void main(String[] args) {
        Context context = null;
        System.out.println("---刚刚到吴国的时候拆第一个锦囊");
        context = new Context(new BackDoor());
        context.operate();

        System.out.println("---刘备乐不思蜀,拆开第二个");

        context = new Context(new GivenGreenLight());
        context.operate();

        System.out.println("---孙权的小兵追了,拆第三个");
        context  = new Context(new BlockEnemy());
        context.operate();
    }
}

//最后运行结果

---刚刚到吴国的时候拆第一个锦囊
找乔国老帮忙,让吴国太给孙权施加压力
---刘备乐不思蜀,拆开第二个
求吴国太开绿灯,放行
---孙权的小兵追了,拆第三个
孙夫人断后,挡住追兵

栗子很简单,也很明了。不再做过多解释,倘若看不太懂,说明你需要补充一下java基础。

策略模式属于行为型模式,在实际开发中,我们要分析一个事物哪些行为是不可变的,哪些行为是可变的。可变行为的开发,日后开发要做到易维护、易扩展。

前人为我们总结出了策略模式,将可变行为抽象,接口化(符合迪米特原则)行为分类定义接口(符合接口分离原则),之后对应实现具体的行为,日后需要扩展时就新建一个类实现行为接口(符合开闭原则)。

三、第二个栗子:

接下来再看一个鸭子的栗子,鸭子的种类很多,有的会飞,飞得高飞得久(所谓飞得好),反之就是飞得差,有的甚至不会飞,就不需要这个行为;有的叫声是咕咕,有的叫声嘎嘎。

首先我们把飞和叫声分别抽象

飞行为抽象

public interface FlyBehavior {
    void fly();
}

//飞得好
public class GoodFlyBehavior implements FlyBehavior {
    private GoodFlyBehavior(){

    }
    public  static  GoodFlyBehavior getInstantiation(){
        return new GoodFlyBehavior();
    }
    @Override
    public void fly() {
        System.out.println("good-----fly");
    }
} 

//飞得差
public class BadFlyBehavior implements FlyBehavior{
    private BadFlyBehavior(){

    }
    //忽略我这里用了反射
    public static BadFlyBehavior getInstantiation(){
        return new BadFlyBehavior();
    }
    @Override
    public void fly() {
        System.out.println("bad----fly");
    }
}

叫声行为抽象:

public interface QuackBehavior {
    void quack();
}

public class GaGaQuackBehavior implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("gaga---quack");
    }
}

public class GuGuQuackBehavior implements QuackBehavior {
    @Override
    public void quack() {
        System.out.println("gugu---quack");
    }
}

接下来我们开始造鸭子了

//首先造一只抽象的鸭子
public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    public Duck(){

    }

    public void fly(){
        if (flyBehavior!=null){
            flyBehavior.fly();
        }
    }

    public void quack(){
        if (quackBehavior!=null){
            quackBehavior.quack();
        }
    }
    public abstract void eat();

    public void swim(){
        System.out.println("小鸭子swim--");
    }
}

//绿头鸭飞得好,嘎嘎叫
public class GreenHeadDuck extends Duck {

    public GreenHeadDuck(){
        flyBehavior = GoodFlyBehavior.getInstantiation();
        quackBehavior = new GaGaQuackBehavior();
    }
    @Override
    public void eat() {
        System.out.println("吃鱼");
    }


}
//红头鸭飞得差,咕咕叫
public class RedHeadDuck extends Duck {

    public RedHeadDuck(){
        flyBehavior = BadFlyBehavior.getInstantiation();
        quackBehavior = new GuGuQuackBehavior();
    }
    @Override
    public void eat() {
        System.out.println("吃鱼---");
    }
}

//蓝头鸦,不会飞,咕咕叫
public class BlueHeadDuck extends Duck {
    public BlueHeadDuck(){
        quackBehavior = new GuGuQuackBehavior();
    }
    @Override
    public void eat() {
        System.out.println("坐着吃");
    }
}


//测试:
public class test1 {
    public static void main(String[] args) {
        Duck green = new GreenHeadDuck();
        green.fly();
        green.quack();
        System.out.println("---------------------------");
        Duck red = new RedHeadDuck();
        red.fly();
        red.quack();
        System.out.println("---------------------------");
        Duck blue = new BlueHeadDuck();
        blue.fly();// 虽然调了fly,但是没打印出什么,看方法逻辑上此方法是空的。
        blue.quack();
    }
}

--------------绿头鸭-------------
good-----fly
gaga---quack
--------------红头鸭-------------
bad----fly
gugu---quack
--------------蓝头鸭-------------
gugu---quack

可以看得出策略模式使用的就是面向对象的继承和多态机制。

策略模式的优点:

  1. 策略可以自由切换
  2. 避免使用多重条件判断
  3. 扩展性好

缺点:

  1. 策略类数量过多
  2. 所有的策略类都需要对外暴露。上层模板必须知道有哪些策略,然后才能决定使用哪个策略,这与迪米特法则是相违背的, 我只是想使用个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义? 这是原装策略模式的一个缺点,幸运的是我们可以使用其他模式来修正这个缺陷,如工厂模式,代理模式或享元模式。

使用场景:

  1. 多个类只有在算法或者行为上稍有不同的场景
  2. 算法需要自由切换的场景
  3. 需要屏蔽算法规则的场景

四、再来举个计算加减的栗子:

同样出自《设计模式之禅(第二版)》的改编。

一个计算器,有两个功能,两个数相加,两个数相减。

public interface Calculator {
    int exec(int a,int b);
}

public class Add implements Calculator {
    @Override
    public int exec(int a, int b) {
        return a+b;
    }
}

public class Sub implements Calculator {
    @Override
    public int exec(int a, int b) {
        return a-b;
    }
}

public class Context {
    private Calculator cal = null;
    public Context(Calculator cal){
        this.cal = cal;
    }

    public int exec(int a,int b){
        return  this.cal.exec(a,b);
    }

}

public class test {
    public static void main(String[] args) {
        Context context = new Context(new Add());
        int result = context.exec(10,10);
        System.out.println(result);

        Context context1 = new Context(new Sub());
        int result1 = context1.exec(20,10);
        System.out.println(result1);
    }
}

来个高端的操作:

public enum Calculator {
    //+
    ADD ("+"){
        public int exec(int a,int b){
            return a+b;
        }
    },
    SUB("-"){
        public int exec(int a,int b){
            return a-b;
        }
    };
    String value = "";

    //定义成员值类型
    private Calculator(String _value){
        this.value = _value;
    }

    //获得枚举成员的值
    public String getValue(){
        return this.value;
    }

    //声明一个抽象函数
    public abstract int exec(int a,int b);
}


public class Client {
    public static void main(String[] args) {
        int add_result = Calculator.ADD.exec(10,20);
        int sub_result = Calculator.SUB.exec(20,10);

        System.out.println(add_result);
        System.out.println(sub_result);
    }
}

是不是感觉很清爽,这就叫策略枚举

策略枚举,是一个非常优秀和方便的模式,但是它受枚举类型的限制,每个枚举项都是public、final、static的,扩展性受了一定的约束,所以在系统开发中,策略枚举一般担当不经常发生变化的角色。

需要注意:

如果系统中的一个策略家族的具体策略数量超过了4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护就会成为一个烫手山芋。

策略模式的精华远不止这些,还待实际开发中运用和体会。

源码地址: https://gitee.com/stefanpy/DesignPattern

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java反射——框架设计的灵魂

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对...

    100000860378
  • java基础thread——java5之后的多线程(浅尝辄止)

    虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个...

    100000860378
  • 观察者模式——心有灵犀

    定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

    100000860378
  • Java基础-day11-接口;多态案例练习

    Java基础-day11-接口&多态案例练习 题目要求1(多态): 定义家类 方法:饲养动物 动物类: 属性:年龄、姓名 方法:吃饭、睡觉 猫类、狗类、猪类均为...

    奋斗蒙
  • 设计模式-装饰者模式

    用户5927264
  • 第76节:Java中的基础知识

    设置环境,安装操作系统,安装备份,就是镜像,jdk配置环境,eclipse下载解压即可使用,下载tomcat

    达达前端
  • 自定义注解与常用设计模式

    注解分为:内置注解,自定义注解。内置注解就是JDK 自带的,而自定义注解则是自己定义的比如许多框架(spring) 用到的

    斯文的程序
  • 设计模式-里氏替换

    先来看个最正宗的定义 如果对每一个类型为S的对象o1,都有类型为T的对 象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变 ...

    yiduwangkai
  • 第25次文章:行为型模式

    关注系统中对象之间的相互交互,研究系统在运行时对象之间的相互通信和协作,进一步明确对象的职责,共有11种模式。

    鹏-程-万-里
  • Java面向对象之抽象类,接口

    抽象类: 含有抽象方法的类被声明为抽象类 抽象方法由子类去实现 含有抽象方法的类必须被声明为抽象类 抽象类被子类继承,子类(如果不是抽象类)必须重写抽象类中...

    二十三年蝉

扫码关注云+社区

领取腾讯云代金券