策略模式(分离算法,选择实现)

公告

如果您是第一次阅读我的设计模式系列文章,建议先阅读设计模式开篇,希望能得到您宝贵的建议。

前言

Alice这个购买机器人女友的事情闹的沸沸扬扬,Alice的爸爸实在不能同意Alice这样胡闹下去,于是Alice的爸爸就去了Alice家(他们平时不住在一起)。

无巧不巧的是那天正好Alice不在家,只有女友Samu在家。于是Alice的爸爸Samu开门表明身份是Alice的爸爸,可是Samu最终仍是拒绝了开门……最后Alice的爸爸在寒冷的北风中站了8个小时后,Alice回来后 Samu才开的门。

正文

Alice的爸爸Samu开开门,我是Alice的爸爸
Samu无法鉴定身份,抱歉无法为你开门。
Alice的爸爸:我出示我的身份证给你看,Samu。(出示了身份证)
Samu无法鉴定身份,抱歉无法为你开门。
Alice的爸爸:……(吐血中)
……(Alice回来了)
AliceSamu开开门。
Samu:欢迎回家,Alice
Alice的爸爸:……(吐血中)

程序员视角

现在要实现身份识别验证后开门的功能,起初Alice并没有把他爸爸的信息告知Samu,所以Samu无法为他开门。后面Alice允许Samu授予他爸爸开门的权限。

在本章中不将重心放在权限层级(如果要考虑权限相对复杂,可考虑享元模式),仅考虑如何识别身份后的分支操作 — — 开门或不开门

当然如果只是IF-ELSE自然是TOO YOUNG TOO SIMPLE

如何实现

策略模式本质:分离算法,选择实现。

参考状态模式 命令模式中的经验,单个命令或状态只处理其自身的逻辑。— — 职责单一原则

为了保证23个GOF都使用同一个GITHUB项目,所以计划23个GOF模式都会使用同一个场景“机器人”作为基础,所以故事之间会有一些关联。

延续前一篇文章“命令行模式”,本次要求能够接收开门命令,所以需要实现接口开门命令

定义开门命令

public class OpenDoorCommandImpl implements ICommand {

    private User user;
    private Machine machine;

    public OpenDoorCommandImpl(User user, Machine machine) {
        this.user = user;
        this.machine = machine;
    }

    @Override
    public void excute() {
        machine.getStrategy(user).operation();
    }
}

同时为了模拟机器人管理开门策略,所以在Machine中作了些许变更。

public class Machine {

    private String name;

    public Machine(String name) {
        this.name = name;
        System.out.println("创建了机器人 " + name);
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
   // 管理了所有的策略(这里是策略接口)
    private HashMap<User, IStrategy> map = new HashMap<>();

    public void configure(User user, IStrategy strategy) {
        map.put(user, strategy);
    }

    public IStrategy getStrategy(User user) {
        return map.get(user);
    }


    @Override
    public String toString() {
        return name;
    }

}

定义命令接收器

public class OpenDoorCommandReceiver {

    private CommandManager invoke = new CommandManager();
    private Machine machine;

    public OpenDoorCommandReceiver(Machine machine) {
        this.machine = machine;
        System.out.printf("机器人%s的接收功能正常开启%n", machine);
    }

    public void onReceive(String command, User user) {
        System.out.printf("机器人%s接收到指令:%s,%s%n", machine, command, user);
        invoke.invoke(new OpenDoorCommandImpl(user, machine));
    }
}

原先在调用播放歌曲命令时,使用适配器适配了String类型作为命令参数。在本例时,暂时不做适配器适配开门命令接口,下一篇文章会仔细描述适配器模式

定义策略的接口:

public interface IStrategy {

    void operation();
}

实现成功开门策略:

public class VerifySuccessStrategy implements IStrategy {
    @Override
    public void operation() {
        System.out.println("验证通过,已将门打开");
    }
}

实现失败开门策略:

public class VerifyFailStrategy implements IStrategy {
    @Override
    public void operation() {
        System.out.println("验证失败,无法为您开门");
    }
}

客户端调用测试

public class Client {

    public static void main(String[] args) {
        User alice = new User("Alice");
        User aliceParent = new User("Alice's Parent");


        Machine machine = new Machine("Samu");
        machine.configure(alice, new VerifySuccessStrategy());
        machine.configure(aliceParent, new VerifyFailStrategy());


        OpenDoorCommandReceiver receiver = new OpenDoorCommandReceiver(machine);
        System.out.println("++aliceParent++");
        receiver.onReceive("开门", aliceParent);
        System.out.println("--aliceParent--");
        System.out.println("++alice++");
        receiver.onReceive("开门", alice);
        System.out.println("--alice--");

    }
}

测试结果

创建了机器人 Samu
机器人Samu的接收功能正常开启
++aliceParent++
机器人Samu接收到指令:开门,com.bookbuf.gof23.User@1b6d3586
验证失败,无法为您开门
--aliceParent--
++alice++
机器人Samu接收到指令:开门,com.bookbuf.gof23.User@28d93b30
验证通过,已将门打开
--alice--

总结

策略模式的本质:分离算法,选择实现

策略模式类图
  • 策略模式是一个比较容易理解和使用的设计模式,策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。

策略模式的优点

  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上* 选择算法或行为,也可以灵活地增加新的算法或行为。
  • 策略模式提供了管理相关的算法族的办法。
  • 策略模式提供了可以替换继承关系的办法。
  • 使用策略模式可以避免使用多重条件转移语句。

策略模式的缺点

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。

在以下情况下可以使用策略模式:

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
    一个系统需要动态地在几种算法中选择一种。
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容