首先感谢《java8实战》一书作者某某某。

需求场景:

为一位果农设计一款软件,可以根据果农的需求筛选出相应的水果。

例如:

  1. 根据颜色筛选
  2. 根据重量筛选
  3. 根据颜色和重量筛选

准备工作

  1. 定义Apple类

    public class Apple
    {
    String color;
    double weight; public Apple(String color,double weight)
    {
    this.color=color;
    this.weight=weight;
    }
    }
  2. 创建苹果库存

    List<Apple> inventory=new ArrayList<Apple>()
    {
    {
    add(new Apple("红色",200));
    add(new Apple("绿色",80));
    add(new Apple("红色",100));
    add(new Apple("绿色",210));
    add(new Apple("红色",105));
    add(new Apple("黄色",180));
    add(new Apple("红色",202));
    add(new Apple("红色",99));
    }
    };

阶段1:值参数化 - 为每一种行为定义一个单独的方法

筛选绿苹果

List<Apple> filterGreenApple(List<Apple> inventory)
{
List<Apple> res=new ArrayList<Apple>();
for(Apple apple: inventory)
{
if("绿色".equals(apple.color))
{
res.add(apple);
}
}
return res;
}

筛选红苹果

List<Apple> filterHeavyApple(List<Apple> inventory)
{
List<Apple> res=new ArrayList<Apple>();
for(Apple apple: inventory)
{
if("红色".equals(apple.color))
{
res.add(apple);
}
}
return res;
}

无需多言,这种方式很糟糕,如果再有其它需求,还得定义更多的这种方法,其中大部分的代码都是重复的。

阶段2:值参数化 - 将条件作为参数

根据颜色筛选苹果

List<Apple> filterAppleByColor(List<Apple> inventory,String color)
{
List<Apple> res=new ArrayList<Apple>();
for(Apple apple: inventory)
{
if(color.equals(apple.color))
{
res.add(apple);
}
}
return res;
}

根据重量筛选苹果

List<Apple> filterAppleByWeight(List<Apple> inventory,double weight)
{
List<Apple> res=new ArrayList<Apple>();
for(Apple apple: inventory)
{
if(apple.weight>=weight)
{
res.add(apple);
}
}
return res;
}

这种为每一种属性定义一个方法的写法,一定程度上解决了代码重复的问题。但是,还不够。

阶段3: 值参数化 - 为属性设置标志

根据颜色或重量筛选苹果

List<Apple> filterApple(List<Apple> inventory,String color,boolean flag)
{
List<Apple> res=new ArrayList<Apple>();
for(Apple apple: inventory)
{
if(flag && color.equals(apple.color) || (!flag && apple.weight>=150))
{
res.add(apple);
}
}
return res;
}

这种方式解决了代码重复的问题,但是,当要筛选的属性很多时,需要添加过多的标志参数,使得单个方法中的代码过于复杂,容易出错。

阶段4: 行为参数化 - 使用接口对选择标准建模

定义行为接口

public interface IApplePredicate
{
boolean test(Apple apple);
}

定义筛选绿色苹果的行为类

public class GreenApplePredicate implements IApplePredicate
{
public boolean test(Apple apple)
{
return "绿色".equals(apple.color);
}
}

定义筛选重量大于150克的苹果的行为类

public class HeavyApplePredicate implements IApplePredicate
{
public boolean test(Apple apple)
{
return apple.weight>=150;
}
}

定义筛选方法,接收行为接口的实例作为参数

List<Apple> filterApples(List<Apple> inventory,IApplePredicate predicate)
{
List<Apple> res=new ArrayList<Apple>();
for(Apple apple: inventory)
{
if(predicate.test(apple))
{
res.add(apple);
}
}
return res;
}

筛选的时候,只需要传递行为类对象即可

List<Apple> greenApples=filterApples(inventory,new GreenApplePredicate());
List<Apple> heavyApples=filterApples(inventory,new HeavyApplePredicate());

通过上面这种方式,可以重复使用同一个方法,给她不同的行为来实现不同的目的。

代码结构清晰,很好。但是,必须为每一种行为定义一个类,还是比较繁琐。

阶段5: 行为参数化 - 使用匿名类避免单独定义行为类

不必为每一种行为都定义一个行为类,然后在使用具体方法的时候,传递它的对象。可以使用匿名类实现同样的效果

List<Apple> greenApples=filterApples(inventory,new IApplePredicate()
{
public boolean test(Apple apple)
{
return "绿色".equals(apple.color);
}
});
List<Apple> heavyApples=filterApples(inventory,new IApplePredicate()
{
public boolean test(Apple apple)
{
return apple.weight>=150;
}
});

阶段6: 行为参数化 - 使用lambda表达式代替匿名类

匿名类固然不错,但是,语法很是丑陋。在java8中,可以使用lambda表达式代替匿名类

List<Apple> greenApples=filterApples(inventory,apple->"绿色".equals(apple.color));
List<Apple> heavyApples=filterApples(inventory,apple -> apple.weight>=150);

阶段7: 行为参数化 - 使用泛型扩展行为的适用范围

目前为止,只能用来过滤苹果,如果想过滤梨子,就得重新定义梨的行为接口。

这样很不方便。这时,就可以使用泛型来解决这个问题。

定义泛型行为接口

public interface IPredicate<T>
{
boolean test(T t);
}

将筛选方法改造为泛型方法

<T> List<T> filter(List<T> elements,IPredicate<T> predicate)
{
List<T> res=new ArrayList<>();
for(T t: elements)
{
if(predicate.test(t))
{
res.add(t);
}
}
return res;
}

这样,筛选方法不止适用于苹果,还适用于梨子,甚至数字类型。

至此,使用行为参数化,完美的解决了需求多变的问题。

Java行为参数化的演进的更多相关文章

  1. 第一章 Java的I/O演进之路

    I/O基础入门 Java的I/O演进 第一章 Java的I/O演进之路 1.1 I/O基础入门 1.1.1 Linux网络I/O模型简介 根据UNIX网络编程对I/O模型的分类,UNIX提供了5中I/ ...

  2. (基础篇 走进javaNIO)第一章-java的i/o演进之路

    Java 是由 SUN公司在 1995 年首先发布 的编程语 言和计算平 台.这基础技术 支持最新 的程序 ,包括 实用程序 .游 戏和业 务应用程序 .J ava 在世界各地 的 8.5  亿 多 ...

  3. Java IO编程全解(一)——Java的I/O演进之路

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7419117.html JDK1.4之前的早期版本,Java对I/O的支持并不完善,开发人员在开发高性能I/O ...

  4. 性能测试十二:jmeter进阶之java请求参数化

    如项目中的ip.端口号之类的,都可以在此代码中定义 public Arguments getDefaultParameters() { // TODO Auto-generated method st ...

  5. JAVA BIO至NIO演进

    主要阐述点: 1.同步/异步 or  阻塞/非阻塞 2.网络模型演进 3.NIO代码示例 一.同步/异步 or  阻塞/非阻塞 同步/异步:核心点在于是否等待结果返回.同步即调用者必须等到结果才返回, ...

  6. Java Socket Server的演进 (一)

    最近在看一些网络服务器的设计, 本文就从起源的角度介绍一下现代网络服务器处理并发连接的思路, 例子就用java提供的API. 1.单线程同步阻塞式服务器及操作系统API 此种是最简单的socket服务 ...

  7. JAVA分布式架构的演进

    系统架构演化历程-初始阶段架构 初始阶段 的小型系统 应用程序.数据库.文件等所有的资源都在一台服务器上通俗称为LAMP 特征:应用程序.数据库.文件等所有的资源都在一台服务器上. 描述:通常服务器操 ...

  8. eventloop & actor模式 & Java线程模型演进 & Netty线程模型 总结

    eventloop的基本概念可以参考:http://www.ruanyifeng.com/blog/2013/10/event_loop.html Eventloop指的是独立于主线程的一条线程,专门 ...

  9. Java模块化规范之争(转载)

    经过近20年的发展,Java语言已成为今日世界上最成功.使用的开发者人数最多的语言之一,Java世界中无数商业的或开源的组织.技术和产品共同构成了一个无比庞大的生态系统. 与大多数开发人员的普遍认知不 ...

随机推荐

  1. linux跨文件复制粘贴

    跨文件是这样的: 复制a.txt的第20行至第30行到b.txt中vi a.txt:2010yy:e b.txtp

  2. 一文学完makefile语法

    一.开始 1.Hello World 新建一个makefile文件,写入如下内容, hello: echo "Hello World" clean: echo "clea ...

  3. [c++] 文件包含

    当一个类用到另一个类时,有两种包含方式,在.h中包含和在.cpp中包含 用到公共类库时,在.h文件中包含(公共类库可视为不变的) 用到项目开发过程中自己或同事写的类时,在.cpp文件中包含(可能根据需 ...

  4. linux服务器默认使用中文字符集zh_CN.UTF-8

    linux服务器默认使用中文字符集zh_CN.UTF-8 一.问题描述和相关概念 linux服务器的字符集设置可能影响到网站页面出现 "???" 等问号乱码,还有可能导致文件中的汉 ...

  5. CentOS 7 设置日期和时间 timedatectl

    CentOS 7 设置日期和时间 在CentOS 6版本,时间设置有date.hwclock命令,从CentOS 7开始,使用了一个新的命令timedatectl. timedatectl [root ...

  6. Linux上使用iSCSI概述

    iSCSI简介 1. scsi和iscsi SCSI技术是存储设备最基本的标准协议,通常需要设备互相靠近并用SCSI总线连接,因此受到物理环境的限制 iSCSI(Internet Small Comp ...

  7. iPhone手机怎么和电脑互传文件,一条数据线搞定

    官方的方法是,通过iTunes进行文件的传输.传个文件还要特意安装个iTunes,实在是麻烦. 其实我们只需要在苹果应用商店app store下载Documents这个文件就可以. 另外,Docume ...

  8. 如何解决在WordPress安装Redis插件时需要输入FTP问题?

    用LAMP或者LNMP搭建Worepress的时候,安装主题或者插件时候,往往提示需要输入FTP服务端信息的问题,其实这是一个坑,可以完全避免的 我们只需在wp-config.php文件最后添加以下代 ...

  9. .NET6系列:Visual Studio 2022 线路图

    系列目录     [已更新最新开发文章,点击查看详细] 在上一篇博客<Visual Studio 2022>中介绍了VS2022的性能改进与重要功能.本文主要介绍在 Visual Stud ...

  10. C#异常处理18条最佳实践

    首先,异常处理应该是系统设计规约的一部分出现在系统设计文档中,而不仅仅是一种技术实现. 作为设计文档的一部分,异常处理应该着眼于系统容错性和稳定性(正如楼主提到的那样).然后在根据这个规约,再来具体讨 ...