简介(Introduction)

之前学习Java8实战时,遇到一个很好的策略模式示例。便想着借着这个示例结合反馈式的方法来,学习策略设计模式,也以便后面反复琢磨学习。

首先我们通过练习,逐步写出符合相应需求的代码,再根据需求进行改进、比较、重写,最终得出一种更灵活的最佳实现。

练习

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

策略模式(Strategy Pattern)

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

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

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

UML

代码实现

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/策略模式/646307?fr=aladdin

[菜鸟教程] http://www.runoob.com/design-pattern/strategy-pattern.html

反馈法学习设计模式(一)——策略模式Strategy Pattern的更多相关文章

  1. 乐在其中设计模式(C#) - 策略模式(Strategy Pattern)

    原文:乐在其中设计模式(C#) - 策略模式(Strategy Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 策略模式(Strategy Pattern) 作者:webabc ...

  2. 8.6 GOF设计模式四: 策略模式… Strategy Pattern

    策略模式… Strategy Pattern  在POS系统中,有时需要实行价格优惠, 该如何处理?  对普通客户或新客户报全价  对老客户统一折扣5%  对大客户统一折扣10%  注:课件 ...

  3. 二十四种设计模式:策略模式(Strategy Pattern)

    策略模式(Strategy Pattern) 介绍定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换.本模式使得算法的变化可独立于使用它的客户. 示例有一个Message实体类,对它的操作有 ...

  4. 设计模式原来如此-策略模式(Strategy Pattern)

    策略模式中体现了两个非常基本的面向对象设计的原则:1.封装变化的概念.2.编程中使用接口,而不是对接口的实现. 策略模式的定义:定义一组算法,将每个算法都封装起来,并使它们之间可以互换.策略模式使这些 ...

  5. 【UE4 设计模式】策略模式 Strategy Pattern

    概述 描述 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法的变化不会影响到使用算法的客户. 套路 Context(环境类) 负责使用算法策略,其中维持了一 ...

  6. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  7. JAVA设计模式之策略模式 - Strategy

    在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改.这种类型的设计模式属于行为型模式. 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 ...

  8. 设计模式 - 策略模式(Strategy Pattern) 具体解释

    策略模式(Strategy Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26577879 本文版权全 ...

  9. HeadFirst设计模式读书笔记(1)-策略模式(Strategy Pattern)

    策略模式(Strategy Pattern): 定义了了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户端. 第一个设计原则:找出应用中可能需要变化之处,把他们独立 ...

随机推荐

  1. EGit使用教程:第一篇 添加工程到版本控制

    配置 确定身份 当每次提交的时候,Git需要跟踪这次提交,确认是哪个用户提交的.用户由 user.name 和 user.email 组成,这个信息包含在 ~/.gitconfig 文件中. ~ 代表 ...

  2. Linux - 设置/取消代理

    export http_proxy=118.210.42.251:44367 或: export https_proxy=118.210.42.251:44367   要取消该设置: unset ht ...

  3. JSP内置对象的实验报告,页面登陆设计

    JSP内置对象的实验报告 一.实验目的: 本实验的目的是让学生掌握怎样在JSP中使用内置对象request.page.response等. 二.实验要求: 编写四个JSP 页面login.jsp.Re ...

  4. jquery.i18n.properties前端国际化解决方案“填坑日记”

    但现在的情况是老的项目并没有使用这类架构.说起国际化,博主几年前就做过,在MVC里面实现国际化有通用的解决方案,主要就是通过资源文件的方式定义多语言.最初接到这个任务,并没有太多顾虑,毕竟这种东西有很 ...

  5. 第4章 同步控制 Synchronization ----互斥器(Mutexes)

    Win32 的 Mutex 用途和 critical section 非常类似,但是它牺牲速度以增加弹性.或许你已经猜到了,mutex 是 MUTual EXclusion 的缩写.一个时间内只能够有 ...

  6. js中+号的另外一种用法

    JavaScript中可以在某个元素前使用 ‘+’ 号,这个操作是将该元素转换成Number类型,如果转换失败,那么将得到 NaN. 所以 +new Date 将会调用 Date.prototype ...

  7. bzoj3156 防御准备 - 斜率优化

    Input 第一行为一个整数N表示战线的总长度. 第二行N个整数,第i个整数表示在位置i放置守卫塔的花费Ai. Output 共一个整数,表示最小的战线花费值. Sample Input 102 3 ...

  8. python下selenium测试报告整合

    使用过一段时间的Robot Framework测试框架,测试之前需要先搭环境,需要安装的东西很多,这一点个人有些排斥. 每一个测试内容对应一个Test_Case,Robot有自己语法格式,如判断.循环 ...

  9. MVC中使用Echart后台加载数据 实现饼图、折线图、全国地图数据,单击双击事件等

    @{ Layout = null; } @if (false) { <script src="~/Js/jquery-easyui-1.5/jquery.min.js"> ...

  10. 关于WPF的验证

    1.ValidationRule 验证 ValidationRule:是通过ValidationRule中的的Validate方法来验证我们绑定的属性.所以我们的用法是继承ValidationRule ...