反馈法学习设计模式(一)——策略模式Strategy Pattern

简介(Introduction)

之前学习Java8实战时,遇到一个很好的策略模式示例。便想着借着这个示例结合反馈式的方法来,学习策略设计模式,也以便后面反复琢磨学习。
首先我们通过练习,逐步写出符合相应需求的代码,再根据需求进行改进、比较、重写,最终得出一种更灵活的最佳实现。

练习

    /** 该类为苹果 */
    class Apple {
        private Float weight;
        private String color;
    }
    /** 该类为苹果过滤器 */
    public class AppleFilter {
        private Set<Apple> apples;
    }
  • 需求一,添加方法使得可以筛选绿苹果
  • 需求二,能够选取各种颜色的苹果
  • 需求三,能够筛选各种颜色, 各种重量的苹果
  • 需求四,将筛选条件进行抽象,能筛选各种属性
  • 需求五,使用匿名类进行改进

策略模式(Strategy Pattern)

对于策略模式,我的理解是行为参数化。行为是指处理频繁变化需求的那段代码。每当需求变化时,就传递不同的行为作为参数进行处理。如此,便是将代码块进行封装,得到可进行应对变化的策略一般。

策略模式,它定义了算法家族。分别封装起来,让它们之间可以相互替换,此模式让算法的变换,不会影响到使用算法的客户端。——《设计模式:可复用面向对象软件的基础》

  • 解决什么矛盾:不同时间应用不同的业务规则;多重条件判断、硬编码所带来的复杂及难以维护
  • 如何用代码实现:每个策略,实现约定的接口及方法。
  • 优点:耦合性低(降低各种策略类与调用者的耦合)、扩展性强、代码简洁(策略封装了变化的条件、避免了多重判断)
  • 缺点:策略类膨胀、代码繁琐

UML

策略模式.PNG

代码实现

package Demo.filter;

/**
 * 该类用于筛选苹果
 * 代码质量要求:更加抽象通用, 更加简洁
 * 以下七次的代码修改也相应反映代码的质量及水平
 *
 * Created by auhnayuil on 17-9-24.
 */
public class FilterApple implements Filter<Apple> {

    /**
     * 第一次需求:选取绿色苹果
     * 该方法纯粹为筛选出绿色苹果
     * 筛选苹果的条件为常量, 很难适应客户或者调用者的需求变化
     */
    public Set<Apple> filterGreenApple(Set<Apple> apples){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if("green".equals(apple.getColor()))
                result.add(apple);
        }
        return result;
    }

    /**
     * 第二次需求变化:能够选取各种颜色的苹果
     * 将颜色提取为方法的参数, 更灵活地适应筛选各种颜色的苹果
     *
     * 一个良好的原则是在编写某个需求多变的代码时, 尝试将其抽象化
     */
    public Set<Apple> filterAppleByColor(Set<Apple> apples, String color){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if(apple.getColor().equals(color))
                result.add(apple);
        }
        return result;
    }

    /**
     * 第三次需求变化:能够筛选各种颜色, 各种重量的苹果
     * 需求变化的因素除了单一元素上变化, 还表现为多元素上变化
     *
     * 一旦多属性被要求组合查询, 进行更复杂的查询时
     * 筛选条件及使用上将会变得非常笨拙及丑陋
     */
    public Set<Apple> filterApples(Set<Apple> apples, String color, Float weight){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if(     apple.getColor().equals(color)
                    && apple.getWeight() > weight)
                result.add(apple);
        }
        return result;
    }

    /**
     * 第四次尝试:将筛选条件进行抽象, 将行为参数化
     * 更高层次的抽象为将选择条件进行建模, 即形成一种可进行选择的通用的策略
     *
     * 模型描述:根据对象的某些属性来返回一个布尔值
     * 类似于"谓词"这样的语义
     *
     * 至于为何要在方法参数中抽象筛选条件为一个接口?
     *
     * 到这里, filterApples的行为仅取决于 Predicate对象所传递的代码, 也就是
     * 所谓的 向一个参数传递了代码, 或者行为参数化了
     *
     * 值需要创建包裹着不同筛选条件的代码块 的Predicate对象就可以实现不同的行为了
     */
    public List<Apple> filterApples(List<Apple> apples, Predicate<Apple> predicate){
        return (List<Apple>) collect(apples, predicate);
    }

    /**
     * 第五尝试:匿名类
     * 没有变量名, 允许你同时声明并实例化一个类
     *
     */
    public Set<Apple> filterApplesByAnonymousClass(Set<Apple> apples){
        return (Set<Apple>) collect(apples, new Predicate<Apple>() {
            @Override
            public boolean test(Apple target) {
                return ("red".equals(target.getColor()) && target.getWeight() > 0.0F);
            }
        });
    }

    /**
     *  第六次尝试:Lambda表达式 以及 抽象结果集
     *  可以改写为以下形式:
     *  filterApplesByLambda(apples, (Apple apple) -> "red".equals(apple.getColor()));
     *
     *  那么如何用Lambda改写一个内部类?
     */
    public Set<Apple> filterApplesByLambda(Collection<Apple> apples, boolean is){
        Set<Apple> result = new HashSet<>();
        for(Apple apple : apples){
            if(is)
                result.add(apple);
        }
        return result;
    }
}

package Demo.filter;

/**
 * 该方法为最基本的过滤器
 * 用于抽象各个过滤器中的循环, 遍历, 收集等重复行为
 * 采用接口的默认方法实现
 *
 * Created by auhnayuil on 17-9-24.
 */
public interface Filter<T> {

    default Collection<T> collect(Collection<T> targets, Predicate<T> predicate) {
        Class<? extends Collection> clazz = targets.getClass();
        Collection result = null;
        try {
             //该部分代码块, 通过反射生成集合的实例对象. 得到一个空的结果集对象
             result = clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        for(T target : targets){
            //循环遍历目标集合, 并且通过接口形成策略判断是否符合过滤器条件
            //收集符合条件的结果
            if(predicate.test(target))
                result.add(target);
        }
        return result;
    }
}
package Demo.predicate;

/**
 * 策略设计模式(Staregy)
 * 定义了一系列的算法族, 并将其封装, 可以相互替换且在运行时选择所需要的合适的"策略"
 * Created by auhnayuil on 17-9-24.
 */
public class AppleRedAndWeightPrdicate implements Predicate<Apple> {

    @Override
    public boolean test(Apple target) {
        return ("red".equals(target.getColor())
                && target.getWeight() > 0.0F);
    }
}

参考链接

[Java8实战] https://book.douban.com/subject/26772632/
[Baidu] https://baike.baidu.com/item/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/646307?fr=aladdin
[菜鸟教程] http://www.runoob.com/design-pattern/strategy-pattern.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,894评论 1 15
  • 1 场景问题# 1.1 报价管理## 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的...
    七寸知架构阅读 5,028评论 9 61
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,409评论 25 707
  • 1 场景问题 1.1 报价管理 向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的客户要...
    4e70992f13e7阅读 3,070评论 2 16
  • 第一保持正念 第二练习幸福的能力 第三练习终结拖延症 第四跟身边的人分享戒烟的方法啊 第五就是接着练习有效沟通了。...
    Miranda分享阅读 297评论 0 0