设计模式——策略模式

206 阅读2分钟

1. 策略模式概述

在策略模式中,一个类的行为或其算法可以在运行时更改。

比如说,在执行一个方法时,我们需要动态地改变其实现逻辑,这里就可以用到策略模式。

(1) 适用情况

某一个方法根据不同的情况需要用到多种执行逻辑时,不要总想着if else,可以优先考虑使用策略模式。

(2) 优点

自由切换执行逻辑,避免代码中出现多重条件判断,用起来感觉很舒服。

(3) 缺点

要实现所有的策略类并向外暴露。

2. 策略模式实例

读过《三国演义》的同学应该还记得这么一段故事:
刘备丧偶之后,周瑜心生一计:用孙权的妹妹向刘备招亲,把他骗到江东,然后软禁起来,逼他拿荆州来换自己。

刘备认为这是个骗局,想要拒绝,诸葛亮却让他安心前去,并给了同去接亲的赵云三个锦囊,让他到时候依次打开。

果然,刘备在江东遇到了各种问题和危险,但由于赵云打开了诸葛亮的锦囊,依计而行,最终都化险为夷。

最后,刘备安然无恙的返回了荆州,而周瑜却“赔了夫人又折兵”。

这就是锦囊妙计的故事,而三个锦囊中的三条计谋,就可以看做是三条策略。

(1) 声明锦囊妙计接口

public interface SilkBagStrategy {
    // 锦囊妙计
    void todo();
}

(2) 实现锦囊中具体的计谋

public class FirstStrategy implements SilkBagStrategy{
    @Override
    public void todo() {
        System.out.println("敲锣打鼓,大肆宣扬接亲的消息。\n");
    }
}
public class SecondStrategy implements SilkBagStrategy{
    @Override
    public void todo() {
        System.out.println("向刘备报告:曹操兴兵来报赤壁之仇,请主公速回荆州!\n");
    }
}
public class ThirdStrategy implements SilkBagStrategy {
    @Override
    public void todo() {
        System.out.println("让刘备向夫人哭诉周瑜施美人计诱杀自己的阴谋。\n");
    }
}

(3) 实现怀揣锦囊的赵云类

public class ZhaoYun {
    private Map<String, SilkBagStrategy> silkBagStrategy = new HashMap<>();

    public ZhaoYun() {
        silkBagStrategy.put("初到江东", new FirstStrategy());
        silkBagStrategy.put("纸醉金迷", new SecondStrategy());
        silkBagStrategy.put("情势危急", new ThirdStrategy());
    }

    /**
     * 该怎么办?
     */
    public void howTodo(String solution) {
        SilkBagStrategy silkBagStrategy = this.silkBagStrategy.get(solution);
        silkBagStrategy.todo();
    }
}

(4) 依计而行

public class StrategyDemo {
    public static void main(String[] args) {
        ZhaoYun zhaoYun = new ZhaoYun();

        System.out.println("刘备一行到了江东,但却无人迎接。");
        zhaoYun.howTodo("初到江东");

        System.out.println("刘备纸醉金迷,不肯回荆州。");
        zhaoYun.howTodo("纸醉金迷");

        System.out.println("周瑜出兵追赶,情势危急。");
        zhaoYun.howTodo("情势危急");
    }
}

运行结果:
image.png

3. 一些思考

赵云每次调用的都是howTodo方法,只不过根据不同的情况传参不同,就可以得到不同的锦囊妙计。
如果需要增加更多的计谋,则只需要创建新的策略类,然后让赵云带上就可以了。

还有一种常用的模式 —— 委派/委托模式,它并不属于23种经典模式中,我个人认为,它其实只是策略模式的变体而已。下面用另一个例子来说明。

在《三国演义》中,还有另一个脍炙人口的故事:
赤壁之战之后,曹操仓皇逃窜,并在途中三次大笑,分别引来了赵云、张飞和关羽,原来他们早就被诸葛亮安排好了。最后关羽念及旧日恩情,义释曹操,放了他一条生路。

这就是“华容道”的故事,而关张赵三位将军,就可以看作是诸葛亮使用了委派模式。

(1) 声明一个将军接口

public interface General {
    // 将军出兵
    void attack();
}

(2) 实现具体的将军类

public class ZhaoYun implements General {
    @Override
    public void attack() {
        System.out.println("曹操第一次笑时,赵云杀出。");
    }
}
public class ZhangFei implements General {
    @Override
    public void attack() {
        System.out.println("曹操第二次笑时,张飞杀出。");
    }
}
public class GuanYu implements General {
    @Override
    public void attack() {
        System.out.println("曹操第三次笑时,关羽杀出。");
    }
}

(3) 实现刘备类,也就是委派者

public class LiuBei {
    ZhuGeLiang zhuGeLiang = new ZhuGeLiang();

    /**
     * 刘备下令进攻,实际上是委派给诸葛亮去安排
     */
    public void command() {
        zhuGeLiang.scheme();
    }
}

(4) 实现诸葛亮类,也就是被委派者

public class ZhuGeLiang {
    private Map<String, General> generals = new HashMap<>();

    public ZhuGeLiang() {
        generals.put("第一次笑", new ZhaoYun());
        generals.put("第二次笑", new ZhangFei());
        generals.put("第三次笑", new GuanYu());
    }

    /**
     * 委派给传
     */
    public void scheme() {
        System.out.println("\n曹操第一次笑...");
        generals.get("第一次笑").attack();
        System.out.println("\n曹操第二次笑...");
        generals.get("第二次笑").attack();
        System.out.println("\n曹操第三次笑...");
        generals.get("第三次笑").attack();

    }
}

(5) 委派者刘备下令出兵

public class DelegatingDemo {
    public static void main(String[] args) {
        LiuBei liuBei = new LiuBei();
        // 刘备下令出兵
        liuBei.command();
    }
}

运行结果:
image.png

在“华容道”中,刘备只下令出兵,但是具体怎么发动攻击他不管,而是委派给了诸葛亮,由诸葛亮给关张赵三位将军下令。 而诸葛亮下令的时候,就用到了策略模式。

参考引用

策略模式:www.runoob.com/design-patt…
委派模式:www.cnblogs.com/ZekiChen/p/…