筛选购物车商品-引出函数式接口妙用
购物车初始化
双11购物节,程序员小马在购物车加入一系列商品
商品种类枚举值:
package lambda.cart;
// 商品类型枚举
public enum SkuCategoryEnum {
CLOTHING(10, "服装类"),
ELECTRONICS(20, "数码类"),
SPORTS(30, "运动类"),
BOOKS(40, "图书类");
// 商品类型编号
private Integer code;
// 商品类型名称
private String name;
SkuCategoryEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
}
商品 sku
package lambda.cart;
import lombok.Data;
/**
* 下单商品信息对象
*/
@Data
public class Sku {
// 编号
private Integer skuId;
// 商品名称
private String skuName;
// 单价
private Double skuPrice;
// 购买个数
private Integer totalNum;
// 总价
private Double totalPrice;
// 商品类型
private Enum skuCategory;
// 构造函数
public Sku(Integer skuId, String skuName, Double skuPrice, Integer totalNum, Double totalPrice, Enum skuCategory) {
this.skuId = skuId;
this.skuName = skuName;
this.skuPrice = skuPrice;
this.totalNum = totalNum;
this.totalPrice = totalPrice;
this.skuCategory = skuCategory;
}
}
购物车中的商品
package lambda.cart;
import java.util.ArrayList;
import java.util.List;
// 购物车服务类
public class CartService {
// 加入到购物车中的商品信息
private static List<Sku> cartSkuList = new ArrayList<Sku>(){
{
add(new Sku(654032, "无人机",
4999.00, 1,
4999.00, SkuCategoryEnum.ELECTRONICS));
add(new Sku(642934, "VR一体机",
2299.00, 1,
2299.00, SkuCategoryEnum.ELECTRONICS));
add(new Sku(645321, "纯色衬衫",
409.00, 3,
1227.00, SkuCategoryEnum.CLOTHING));
add(new Sku(654327, "牛仔裤",
528.00, 1,
528.00, SkuCategoryEnum.CLOTHING));
add(new Sku(675489, "跑步机",
2699.00, 1,
2699.00, SkuCategoryEnum.SPORTS));
add(new Sku(644564, "Java编程思想",
79.80, 1,
79.80, SkuCategoryEnum.BOOKS));
add(new Sku(678678, "Java核心技术",
149.00, 1,
149.00, SkuCategoryEnum.BOOKS));
add(new Sku(697894, "算法",
78.20, 1,
78.20, SkuCategoryEnum.BOOKS));
add(new Sku(696968, "TensorFlow进阶指南",
85.10, 1,
85.10, SkuCategoryEnum.BOOKS));
}
};
// 获取商品信息列表
public static List<Sku> getCartSkuList() {
return cartSkuList;
}
}
筛选数码产品
接下来女友小丽需要检查其购物车,决定哪些可以买,毕竟经济大权掌握在小丽手中。现在小丽要让小马筛选出所有的数码类商品
首先,要新增数码类产品的筛选方法
/**
* Version 1.0.0
* 找出购物车中所有电子产品
* @param cartSkuList
* @return
*/
public static List<Sku> filterElectronicsSkus(List<Sku> cartSkuList) {
List<Sku> result = new ArrayList<Sku>();
for (Sku sku : cartSkuList) {
if (SkuCategoryEnum.ELECTRONICS.equals(sku.getSkuCategory())) {
result.add(sku);
}
}
return result;
}
单元测试调用方法
package lambda.cart;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.List;
public class Version1Test {
@Test
public void filterElectronicsSkus() {
List<Sku> cartSkuList = CartService.getCartSkuList();
// 查找购物车中数码类商品
List<Sku> result = CartService.filterElectronicsSkus(cartSkuList);
System.out.println(JSON.toJSONString(result, true));
}
}
根据类型筛选产品
这种硬编码方式并不好,小丽要换一个类型,小马就要再写一个方法,可以将方法封装为根据类型筛选,让小丽自己传参
/**
* Version 2.0.0
* 根据传入商品类型参数,找出购物车中同种商品类型的商品列表
* @param cartSkuList
* @param category
* @return
*/
public static List<Sku> filterSkusByCategory(List<Sku> cartSkuList, SkuCategoryEnum category) {
List<Sku> result = new ArrayList<Sku>();
for (Sku sku : cartSkuList) {
if (category.equals(sku.getSkuCategory())) {
result.add(sku);
}
}
return result;
}
测试调用
package lambda.cart;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.List;
public class Version2Test {
@Test
public void filterSkusByCategory() {
List<Sku> cartSkuList = CartService.getCartSkuList();
List<Sku> result = CartService.filterSkusByCategory(cartSkuList, SkuCategoryEnum.BOOKS);
System.out.println(JSON.toJSONString(result, true));
}
}
添加总价维度
有些商品的价格很贵,小丽想要新增总价维度,比如筛选出价格大于 2000 的商品,小马只好继续写代码...
/**
* Version 3.0.0
* 通过商品类型或总价来过滤商品
* @param cartSkuList
* @param category
* @param totalPrice
* @param categoryOrPrice true:根据商品类型 false: 根据商品总价
* @return
*/
public static List<Sku> filterSkus(List<Sku> cartSkuList, SkuCategoryEnum category, Double totalPrice, Boolean categoryOrPrice) {
List<Sku> result = new ArrayList<Sku>();
for (Sku sku : cartSkuList) {
if (
(categoryOrPrice && category.equals(sku.getSkuCategory())) || // 根据商品类型判断
(!categoryOrPrice && sku.getTotalPrice() > totalPrice) // 根据商品总价判断
) {
result.add(sku);
}
}
return result;
}
测试调用
package lambda.cart;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.List;
public class Version3Test {
@Test
public void filterSkus() {
List<Sku> cartSkuList = CartService.getCartSkuList();
// 根据类型
List<Sku> result1 = CartService.filterSkus(cartSkuList, SkuCategoryEnum.BOOKS, 0.0, true);
System.out.println(JSON.toJSONString(result1, true));
// 根据总价
List<Sku> result2 = CartService.filterSkus(cartSkuList, null, 2000.0, false);
System.out.println(JSON.toJSONString(result2, true));
}
}
将行为参数化
这么折腾下来,也不是事,代码越来越丑,再加多几个条件,将会变得难以维护
能不能将这些判断条件抽象化,服务中只管最终的判断结果,而不管究竟是如何判断的?
/**
* version 4.0.0
* 根据不同的 sku 判断标准,对 sku 列表进行过滤
* @param cartSkuList
* @param predicate 不同的 sku 判断标准策略
* @return
*/
public static List<Sku> filterSkus(List<Sku> cartSkuList, SkuPredicate predicate) {
List<Sku> result = new ArrayList<Sku>();
for (Sku sku : cartSkuList) {
// 根据不同的 sku 判断标准策略,对 sku 进行判断
if (predicate.test(sku)) {
result.add(sku);
}
}
return result;
}
即,不同的 sku 提供不同的判断标准,服务类只获取结果
sku 的选择谓语是一个抽象的接口
package lambda.cart;
/**
* sku 选择谓词
*/
public interface SkuPredicate {
/**
* 选择判断标准
* @param sku
* @return
*/
boolean test(Sku sku);
}
具体的 sku 去实现此接口
package lambda.cart;
/**
* 对 sku 的商品类型为图书类的判断标准
*/
public class SkuBooksCategoryPredicate implements SkuPredicate {
@Override
public boolean test(Sku sku) {
return SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory());
}
}
package lambda.cart;
/**
* 对 sku 的总价是否超出 2000 作为判断标准
*/
public class SkuTotalPricePredicate implements SkuPredicate {
@Override
public boolean test(Sku sku) {
return sku.getTotalPrice() > 2000;
}
}
现在要调用大于 2000 的商品,代码如下:
package lambda.cart;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.List;
public class Version4Test {
@Test
public void filterSkus() {
List<Sku> cartSkuList = CartService.getCartSkuList();
// 过滤总价大于 2000 的商品
List<Sku> result2 = CartService.filterSkus(cartSkuList, new SkuTotalPricePredicate());
System.out.println(JSON.toJSONString(result2, true));
}
}
经此拆分,CartService 的代码就很简洁。将要判断的具体逻辑作为参数传递,即:行为参数化
匿名类
以上的行为参数化过于复杂,要实例化很多类,但思想是很优秀的,我们可以传递匿名类,就不用去写那么多的实现类
package lambda.cart;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.List;
public class Version5Test {
@Test
public void filterSkus() {
List<Sku> cartSkuList = CartService.getCartSkuList();
// 用匿名类传递参数
// 不用对接口进行实现,不需要写一堆多余的类
List<Sku> result2 = CartService.filterSkus(cartSkuList, new SkuPredicate() {
@Override
public boolean test(Sku sku) {
return sku.getSkuPrice() > 3000;
}
});
System.out.println(JSON.toJSONString(result2, true));
}
}
lambda 表达式进行优化
还可以再用 lambda 表达式进行优化
package lambda.cart;
import com.alibaba.fastjson.JSON;
import org.junit.Test;
import java.util.List;
public class Version6Test {
@Test
public void filterSkus() {
List<Sku> cartSkuList = CartService.getCartSkuList();
List<Sku> result = CartService.filterSkus(cartSkuList, (Sku sku) -> sku.getSkuPrice() > 1000);
System.out.println(JSON.toJSONString(result, true));
}
}
至此,CartService 不需要再去定义复杂的判断条件,调用方直接用 lambda 表达式进行传参即可
内置的函数式接口
jdk 已经为我们定义了现成的函数式接口
在 java.util.function 包下有大量的内置函数式接口,如:
-
XxxFunction:这类接口中通常包含一个apply()抽象方法,该方法对参数进行处理、转换(apply()方法的处理逻辑由Lambda表达式来实现),然后返回一个新的值。该函数式接口通常用于对指定数据进行转换处理
-
XxxConsumer:这类接口中通常包含一个accept()抽象方法,该方法与XxxFunction接口中的apply()方法基本相似,也负责对参数进行处理,只是该方法不会返回处理结果
-
XxxxPredicate:这类接口中通常包含一个test()抽象方法,该方法通常用来对参数进行某种判断(test()方法的判断逻辑由Lambda表达式来实现),然后返回一个boolean值。该接口通常用于判断参数是否满足特定条件,经常用于进行筛滤数据
-
XxxSupplier:这类接口中通常包含一个getAsXxx()抽象方法,该方法不需要输入参数,该方法会按某种逻辑算法(getAsXxx ()方法的逻辑算法由Lambda表达式来实现)返回一个数据
自定义函数式接口
此外,我们也可以自定义函数式接口
package lambda.cart.file;
@FunctionalInterface
public interface FileConsumer {
/**
* 函数式接口抽象方法
* @param fileContent
*/
void fileHandler(String fileContent);
}
文件服务类
package lambda.cart.file;
import org.apache.commons.lang3.text.StrBuilder;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* 文件服务类
*/
public class FileService {
/**
* 通过 url 获取本地文件内容
* @param url
* @param fileConsumer
*/
public void fileHandle(String url, FileConsumer fileConsumer) throws IOException {
// 创建文件读取流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(url)));
// 定义行变量和内容
String line;
StringBuilder stringBuilder = new StringBuilder();
// 循环读取文件内容
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line + "\n");
}
// 调用函数式接口方法,将文件内容传递给 lambda 表达式,实现业务逻辑
fileConsumer.fileHandler(stringBuilder.toString());
}
}
测试调用
package lambda.file;
import lambda.cart.file.FileService;
import org.junit.Test;
import java.io.IOException;
public class FileServiceTest {
@Test
public void fileHandle() throws IOException {
FileService fileService = new FileService();
// 通过 lambda 表达式,打印文件内容
fileService.fileHandle("/Users/senlongzhong/code/demo/javaDemo/src/test/java/lambda/file/FileServiceTest.java", fileContent -> System.out.println(fileContent));
}
}
- 上一篇: spring 文档都不读,就想读懂源码?
- 下一篇: 流式编程开场
