背景
有这样一种需求,要在一个列表的数据中进行筛选.筛选规则和筛选优先级时常改变(不得不吐槽产品同学才华横溢,富有创意).如果不采取合适的方案去应对,而且你的代码功力又不是很强,那很可能每周加班是你的宿命了.
场景
假设现在有一个产品类,属性如下,有类型,有单价,有让利三个字段.
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@ToString
public class Product {
private String type;
private String price;
private String benefit;
}
产品类型有AB两种,产品A的优先级大于B,当产品的优先级一致时,价格低的优先级更高,当价格一致时,让利大的优先级更高.现在给你一个列表的数据,你要筛选出优先级最高的数据,如果有多个,就取第一个.我们看看有几种解决方案.
方案一:硬撸
public class Demo01 {
public static void main(String[] args) {
Product p1 = new Product("A", 10, 5);
Product p2 = new Product("A", 10, 6);
Product p3 = new Product("B", 10, 6);
Product p4 = new Product("B", 9, 6);
Product p5 = new Product("A", 10, 6);
List<Product> productList = Lists.newArrayList(p1, p2, p3, p4, p5);
Product resultProduct = new Product("B", Integer.MAX_VALUE, Integer.MAX_VALUE);
for (Product product : productList) {
// A 和 B 比较.A更小
if (product.getType().compareTo(resultProduct.getType()) < 0) {
resultProduct = product;
continue;
}
if (product.getType().equals(resultProduct.getType())) {
// 选择价格低的
if (product.getPrice() < resultProduct.getPrice()) {
resultProduct = product;
continue;
}
if (product.getPrice().intValue() == resultProduct.getPrice().intValue()) {
// 选择让利大的
if (product.getBenefit() > resultProduct.getBenefit()) {
resultProduct = product;
}
}
}
}
System.out.println(resultProduct);
}
}
在这种仅仅只有三个筛选条件的情况,这种方式的代码已经十分臃肿,而且,如果维护这份代码,在需求频繁更改的情况下是极其容易出错的.更何况生产环境只会更复杂.当然你可以选择把他们抽成一个方法.然后写单元测试.但是后期维护工作依然巨大.
方案二: 使用List的sort方法进行排序
使用List.sort方法.传入Compartor对象,随后取出排序后的第一个元素.代码如下:
public class Demo02 {
public static void main(String[] args) {
Product p1 = new Product("A", 10, 5);
Product p2 = new Product("A", 10, 6);
Product p3 = new Product("B", 10, 6);
Product p4 = new Product("B", 9, 6);
Product p5 = new Product("A", 10, 6);
List<Product> productList = Lists.newArrayList(p1, p2, p3, p4, p5);
productList.sort((pro1, pro2) -> {
if (pro1.getType().compareTo(pro2.getType()) < 0) {
return -1;
}
if (pro1.getType().equals(pro2.getType())) {
if (pro1.getPrice() < pro2.getPrice()) {
return -1;
}
if (pro1.getPrice().intValue() == pro2.getPrice().intValue()) {
return pro2.getBenefit() - pro1.getBenefit();
}
return 1;
}
return 1;
});
System.out.println(productList.get(0));
}
}
这种方式本质上和上面的方案一差不多,应对变化的能力差.但也是这种情况下常见的解决方案.
方案三:责任链
上面的两种方案,本质上来说都是面向过程的开发方式.在这个场景下,实际上我们就是在进行一个操作:过滤.所以我们可以给过滤这个动作建模,然后针对各种不同的过滤条件进行实现.通过组装各种过滤实现达到扩展和调整的目标.实现如下:
给过滤动作建模:
public interface IFilter<T> {
List<T> filter(List<T> data);
}
实现按类型筛选的过滤器:
public class TypeFilter implements IFilter<Product> {
private Ordering<Product> ordering = new Ordering<Product>() {
@Override
public int compare(Product product, Product t1) {
return product.getType().compareTo(t1.getType());
}
};
@Override
public List<Product> filter(List<Product> data) {
if (CollectionUtils.isEmpty(data)) return data;
if (data.size() == 1) return data;
List<Product> result = Lists.newArrayList();
List<Product> sortResult = ordering.sortedCopy(data);
String type = sortResult.get(0).getType();
for (Product product : sortResult) {
if (type.equals(product.getType())) {
result.add(product);
} else {
break;
}
}
return result;
}
}
按价格筛选的过滤器
public class PriceFilter implements IFilter<Product> {
private Ordering<Product> ordering = new Ordering<Product>() {
@Override
public int compare(Product product, Product t1) {
return product.getPrice() - t1.getPrice();
}
};
@Override
public List<Product> filter(List<Product> data) {
if (CollectionUtils.isEmpty(data)) return data;
if (data.size() == 1) return data;
List<Product> result = Lists.newArrayList();
List<Product> sortResult = ordering.sortedCopy(data);
int price = sortResult.get(0).getPrice();
for (Product product : sortResult) {
if (price == product.getPrice()) {
result.add(product);
} else {
break;
}
}
return result;
}
}
按让利筛选的过滤器
public class BenefitFilter implements IFilter<Product> {
private Ordering<Product> ordering = new Ordering<Product>() {
@Override
public int compare(Product product, Product t1) {
return t1.getBenefit() - product.getBenefit();
}
};
@Override
public List<Product> filter(List<Product> data) {
if (CollectionUtils.isEmpty(data)) return data;
if (data.size() == 1) return data;
List<Product> result = Lists.newArrayList();
List<Product> sortResult = ordering.sortedCopy(data);
int benefit = sortResult.get(0).getBenefit();
for (Product product : sortResult) {
if (benefit == product.getBenefit()) {
result.add(product);
} else {
break;
}
}
return result;
}
}
客户端代码
public class Demo03 {
public static void main(String[] args) {
Product p1 = new Product("A", 10, 5);
Product p2 = new Product("A", 10, 6);
Product p3 = new Product("B", 10, 6);
Product p4 = new Product("B", 9, 6);
Product p5 = new Product("A", 10, 6);
List<Product> productList = Lists.newArrayList(p1, p2, p3, p4, p5);
List<IFilter<Product>> filterChain = Lists.newArrayList(
new TypeFilter(), new PriceFilter(), new BenefitFilter()
);
for(IFilter<Product> filter : filterChain){
productList = filter.filter(productList);
}
System.out.println(productList.get(0));
}
}
使用这种方式似乎代码量更多,但是扩展性和维护性上都有了质的提升.
加规则
只需要写一个IFilter的实现类,并在FilterChain上放到一个合适的位置即可.
不加规则,调整权重
只需要修改FilterChain上的位置即可.
还能做的更好吗?
可以针对产品的需求将所有的Filter实现类都实例化,做成配置,接入到公司的配置管理系统.产品在已有规则下的权重调整只需要在配置系统中修改即可,不需要重新修改代码,编译,部署.这样下来,你还需要加班吗?