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

公告

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

前言

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:欢迎回家,AliceAlice的爸爸:……(吐血中)

程序员视角

现在要实现身份识别验证后开门的功能,起初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--

总结

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

策略模式类图

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

策略模式的优点

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

策略模式的缺点

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

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

  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 一个系统需要动态地在几种算法中选择一种。
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习算法与Python学习

python初学者的建议

Python是一种非常富有表现力的语言。它为我们提供了一个庞大的标准库和许多内置模块,帮助我们快速完成工作。然而,许多人可能会迷失在它提供的功能中,不能充分利用...

42912
来自专栏腾讯NEXT学位

提升代码可读性的 10 个技巧

3746
来自专栏熊二哥

.NET工作准备--01前言

01应聘须知(已过时) -1.了解软件开发大环境。 -2.准备简历:不宜超过一页,永远准备中文,模板。 -3.渠道:3大网站,中华英才,前程无忧(51job最...

2128
来自专栏java一日一条

编写高质量代码的思考

最近在看《代码大全》,可以说是一本软件开发的百科全书,特别厚,但是干货也很多。平时写代码,代码规范是一个最低的要求(很多老代码连最低要求都达不到),为什么要这样...

1032
来自专栏企鹅号快讯

Python 相较于Java 而言,有什么优势?

最近在后台,有很多的同学私聊我说,Python和Java.,哪个具有前景呢?今天我们就来简单的讲一下这两者的区别: 我认为C, Java跟Python都是非常成...

2516
来自专栏GreenLeaves

Apater适配器模式(结构型模式)

what is Apater?适配,这个概念在生活中无处不在,比如你的iphone 4手机充电器坏了,这是时候只有一个iphone 8的充电器,两个充电器的头并...

722
来自专栏java一日一条

编写高质量代码的思考

最近在看《代码大全》,可以说是一本软件开发的百科全书,特别厚,但是干货也很多。平时写代码,代码规范是一个最低的要求(很多老代码连最低要求都达不到),为什么要这样...

952
来自专栏Python爬虫与算法进阶

爬虫之全站爬取方法

其实这个很好理解。比如说知乎,一个大V有100W粉丝,从这个大V出发,抓取粉丝的粉丝,一直循环下去。(可能是个死循环)

3733
来自专栏Python研发

设计模式 -- 常用设计模式

                                  ——可复用面向对象软件的基础

3011
来自专栏极客猴

“干将莫邪” —— Xpath 与 lxml 库

前面的文章,我们已经学会正则表达式以及 BeautifulSoup库的用法。我们领教了正则表达式的便捷,感受 beautifulSoup 的高效。本文介绍也是内...

871

扫码关注云+社区

领取腾讯云代金券