设计模式是枯燥的,但设计模式却是很有必要的,这里,我只能以自己工作中的实际经验为基础,讲述什么是 策略模式 了。后面,我将继续补充其他的设计模式,欢迎阅读。
我是一名 Android 工程师,而全栈之旅本来的意义在于记录我一个 Android 工程师如何变身全栈的,不过现在却成了记录我个人生活与分享前沿IT技术了,也算没忘初心。设计模式系列博客将用通俗易懂的方式揭开设计模式的面纱,本文是这系列的第一篇,下面我们进入主题。
我们面试也好,平时讨论也罢,总会提到设计模式,那么什么是设计模式呢,我相信无论是书本的介绍还是维基或者百度百科都没有说明白,或者说他们只是给了一个官方的解释,我们需要一个通俗易懂的,以我的理解,设计模式就是解决问题的一种方式,无论是那个设计模式,其本质是为了解决相应的问题,或者说,每个设计模式都有其具体的使用场景,即能解决的具体问题,所以,说的简单点,设计模式就是解决问题的一种方式,当然是解决编程中遇到的问题。下面,我们来看看策略模式吧。
我曾经开发过一款骚扰拦截的 APP,它具有多种拦截模式可选,比如自定义模式,智能拦截模式,免打扰模式,仅拦截黑名单模式等,我们来看看如何实现这样的功能呢,我最初的代码如下:
public static void checkAndBlock(String phoneNumber) { if (isSmartMode()) { dealSmartMode(phoneNumber); return; } if (blockBlackListMode()) { dealBlackListMode(phoneNumber); return; } if (blockAllMode()) { dealblockAllMode(phoneNumber); return; } if (customMode()) { dealCustomMode(phoneNumber); } } private static void dealBlackListMode(String phoneNumber) { // TODO } private static void dealSmartMode(String phoneNumber) { // TODO } private static void dealblockAllMode(String phoneNumber) { // TODO } private static void dealCustomMode(String phoneNumber) { // TODO } private static boolean isSmartMode() { String mode = currentMode(); return mode == "smart"; } private static boolean blockBlackListMode() { String mode = currentMode(); return mode == "blackList"; } private static boolean blockAllMode() { String mode = currentMode(); return mode == "blockAll"; } private static boolean customMode() { String mode = currentMode(); return mode == "custom"; } private static String currentMode() { return "default"; } private static boolean isInBlackList(String phoneNumber) { // TODO return false; }
我相信这也是绝大部分人写的代码了,甚至可能有人会在checkAndBlock 里 写很多逻辑,而不是拆分,这也正常,那上述代码有啥缺点呢?我觉得有以前缺点:
1、每增加一个拦截模式就需要修改这个类,我们应该抽离需要修改的代码,把它和不变化的代码分开,而不是把变化的部分和不变化的部分混在一起。
2、电话拦截和短信拦截在逻辑判断上是一样的,但执行拦截的操作是不一样的,比如电话拦截要先静音,然后判断是否放行,如果是则响铃,如果不是则挂断,而短信则是如果拦截就写到拦截列表,否则写到信箱并提示用户。但是判断是否在黑名单,是否是联系人,是否是大家标记的骚扰,却是一样的。
1、分析问题,实际就是四个模式,但他们是互斥的,只能用其中一种;
2、为了方便扩展,我们写4个类分别用于处理这4种情况,这样子变化的部分和不变化的部分就彻底分离了;
3、写一个接口,Blocker,它有一个方法,doBlock,然后新建 CallBlocker、SmsBlocker 分别继承它,并实现各自的拦截逻辑;
4、在电话或者短信进来的时候,先判断是否需要拦截,然后调用相应的 Blocker 进行拦截。
这部分代码,我已上传至码云,具体可以参考:design pattern
最后,我们再来看看什么是策略模式,至于名字,我觉得不重要,这个设计模式的本质是定义了一组可以互相替换的算法,让算法独立于使用它的具体类。还有一个经典的例子,即 Head First 设计模式 里提到的 鸭子,鸭子有各种各样的鸭子,而不同的鸭子又有不同的飞行以及叫的属性,于是我可以定义一个 鸣叫的接口,有的鸭子呱呱叫,有的鸭子垭口不叫,有的鸭子不会飞,有的鸭子可以飞等等,所以还可以定义一个飞行的接口,然后各种鸭子则继承鸭子类并实现其抽象方法,比如叫以及飞行,不同的鸭子在叫和飞行的时候再根据需要使用不同的叫以及飞行的行为即可。
总结起来,策略模式的使用场景是 有一些类似的可以互相替换的需求,这时候可以把每种需求封装为一个具体的类,而这个类的某些行为可能和其他类的某些行为是一样的,这时候同样可以将这些行为也封装起来,于是,我们有了几组算法,而每组算法间是可以互相替换的。比如 免打扰模式 和 智能拦截 模式是可以互相替换的,再比如,用脚走路和飞着走路也是可以互相替换的。
我们应该抽离变化的部分,使其与不变化的部分分离开来,此外,我们应该面向接口编程,而不是面向对象,面向对象把具体对象与相关类耦合在一起,而面向接口则很好的解耦了,最后,我们应多使用组合,少使用继承,这里说的组合并不是组合模式,而是 鸭子飞行的时候,你可以通过 new 一个 飞行类来辅助完成,而不是继承这个类。