过滤器模式


定义

模式引入

首先,什么是过滤器模式?这种模式生活中比较常见,比如移动推出某项优惠套餐,但是套餐可使用的用户群体有限,必须满足入网 5 年以上这种条件,我们可以将 “入网五年” 作为客户群体的过滤条件,这种就是简单的过滤器模式应用。

又比如,现在的垃圾分类,很多城市从上海开始,已经运行起来,着实让人头大,这种垃圾过滤分类的思想其实本身就是一种过滤模式。再比如我们设计日志系统时,并非所有日志都要收集,而是选择性过滤收集,这种实现了日志过滤的东西本身就是过滤器模式的一种体现。讲了这么多,到底什么是过滤器模式?

模式定义

过滤器模式(Filter Pattern)又称为标准模式 (Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过运算逻辑以解耦的方式将它们联系起来。这种类型的设计模式属于结构型模式,说白了,就是按条件筛选一组对象出来。

目的:使用不同标准来过滤一组对象
实现:制定不同的规则来实现过滤,然后对过滤结果进行分组

组成角色

过滤器模式一般有如下角色:

  • 抽象过滤器角色(AbstractFilter):负责定义过滤器的实现接口,具体的实现还要具体过滤器角色去参与,客户端可以调用抽象过滤器角色中定义好的方法,将客户端的所有请求委派到具体的实现类去,从而让实现类去处理;
  • ConcreteFilter(具体过滤器角色):该角色负责具体筛选规则的逻辑实现,最后再返回一个过滤后的数据集合,标准的过滤器只对数据做过滤,当然也可以对集合中的数据做某项处理,再将处理后的集合返回;
  • Subject(被过滤的主体角色):一个软件系统中可以有一个或多个目标角色,在具体过滤器角色中会对指定感兴趣的目标进行处理,以确保后面的数据确实是我想要的

过滤器延伸 —— 管道和过滤器

这里的管道,其实比较好理解,就类似于一根根的水管或者一个个的流水线,将一个个的过滤器连接起来,形成一个过滤器链(过滤器链可以携带多个过滤器,并且可以以自定义顺序执行它们),原始数据经过一道道的加工工序,最后形成我们需要的数据或产品。一般情况下我们还存在一个过滤器管理器的角色,过滤器管理器负责管理过滤器和过滤器链。

应用场景

下面我们通过一个例子来了解下过滤器模式,我们就以垃圾分类为例进行说明,使用不同的垃圾桶进行垃圾分类过滤,“垃圾” 就是我们的被过滤的角色,垃圾桶可以充当我们的绝体过滤器角色,实例的类图如下:

首先是 “垃圾” 类,也就是我们的被过滤的主体角色:

public class Rubbish {

    private String name; // 垃圾名称

    private boolean isHarm; // 是否有害垃圾
    private boolean isRecycled; // 是否可回收
    private boolean isDry; // 是否干垃圾
    private boolean isWet; // 是否湿垃圾

    public Rubbish(String name, boolean isHarm, boolean isRecycled, boolean isDry, boolean isWet) {
        this.name = name;
        this.isHarm = isHarm;
        this.isRecycled = isRecycled;
        this.isDry = isDry;
        this.isWet = isWet;
    }
    // ... getter、setter省略,或者直接使用lombok
}

然后是我们的过滤标准的接口,即抽象过滤器角色:

public interface Criteria {
    // 定义过滤的标准
    List<Rubbish> rubbishFilter(List<Rubbish> rubbishes);
}

我们继续创建实现了 Criteria 接口的几个实体类:

// 干垃圾
public class DryRubbishCriteria implements Criteria{

    @Override
    public List<Rubbish> rubbishFilter(List<Rubbish> rubbishes) {
        List<Rubbish> rubbishList = new ArrayList<>();
        for (Rubbish rubbish: rubbishes) {
            // 这里只过滤出所有干垃圾
            if (rubbish.isDry()) {
                rubbishList.add(rubbish);
            }
        }
        return rubbishList;
    }
}
...
// 有害垃圾
public class HarmfulRubbishCriteria implements Criteria{

    @Override
    public List<Rubbish> rubbishFilter(List<Rubbish> rubbishes) {
        List<Rubbish> rubbishList = new ArrayList<>();
        for (Rubbish rubbish: rubbishes) {
            // 这里只过滤出所有有害垃圾
            if (rubbish.isHarm()) {
                rubbishList.add(rubbish);
            }
        }
        return rubbishList;
    }
}
...
// 可回收垃圾
public class RecycledRubbishCriteria implements Criteria{

    @Override
    public List<Rubbish> rubbishFilter(List<Rubbish> rubbishes) {
        List<Rubbish> rubbishList = new ArrayList<>();
        for (Rubbish rubbish: rubbishes) {
            // 这里只过滤出所有可回收垃圾
            if (rubbish.isRecycled()) {
                rubbishList.add(rubbish);
            }
        }
        return rubbishList;
    }
}
...
// 湿垃圾
public class WetRubbishCriteria implements Criteria{

    @Override
    public List<Rubbish> rubbishFilter(List<Rubbish> rubbishes) {
        List<Rubbish> rubbishList = new ArrayList<>();
        for (Rubbish rubbish: rubbishes) {
            // 这里只过滤出所有湿垃圾
            if (rubbish.isWet()) {
                rubbishList.add(rubbish);
            }
        }
        return rubbishList;
    }
}

使用不同的标准(Criteria)来过滤 Rubbish 对象的列表:

public class Main {

    public static void main(String[] args) {
        // 原始数据集合
        List<Rubbish> rubbishList = new ArrayList<>();
        rubbishList.add(new Rubbish("果壳", false, false, true, false));
        rubbishList.add(new Rubbish("陶瓷", false, false, true, false));
        rubbishList.add(new Rubbish("菜根菜叶", false, false, false, true));
        rubbishList.add(new Rubbish("果皮", false, false, false, true));
        rubbishList.add(new Rubbish("水银温度计", true, false, false, false));
        rubbishList.add(new Rubbish("电池", true, false, false, false));
        rubbishList.add(new Rubbish("灯泡", true, false, false, false));
        rubbishList.add(new Rubbish("废纸塑料", false, true, false, false));
        rubbishList.add(new Rubbish("金属和布料", false, true, false, false));
        rubbishList.add(new Rubbish("玻璃", false, true, false, false));

        // 四种不同的过滤标准
        Criteria dryRubbishCriteria = new DryRubbishCriteria();
        Criteria wetRubbishCriteria = new WetRubbishCriteria();
        Criteria harmfulRubbishCriteria = new HarmfulRubbishCriteria();
        Criteria recycledRubbishCriteria = new RecycledRubbishCriteria();

        System.out.println("干垃圾: ");
        printRubbishes(dryRubbishCriteria.rubbishFilter(rubbishList));

        System.out.println("湿垃圾: ");
        printRubbishes(wetRubbishCriteria.rubbishFilter(rubbishList));

        System.out.println("有害垃圾: ");
        printRubbishes(harmfulRubbishCriteria.rubbishFilter(rubbishList));

        System.out.println("可回收垃圾: ");
        printRubbishes(recycledRubbishCriteria.rubbishFilter(rubbishList));
    }

    private static void printRubbishes(List<Rubbish> rubbishes) {
        for (Rubbish rubbish: rubbishes) {
            System.out.println(rubbish);
        }
    }

}

结果如下:

干垃圾:
Rubbish{name='果壳', isHarm=false, isRecycled=false, isDry=true, isWet=false}
Rubbish{name='陶瓷', isHarm=false, isRecycled=false, isDry=true, isWet=false}

湿垃圾:
Rubbish{name='菜根菜叶', isHarm=false, isRecycled=false, isDry=false, isWet=true}
Rubbish{name='果皮', isHarm=false, isRecycled=false, isDry=false, isWet=true}

有害垃圾:
Rubbish{name='水银温度计', isHarm=true, isRecycled=false, isDry=false, isWet=false}
Rubbish{name='电池', isHarm=true, isRecycled=false, isDry=false, isWet=false}
Rubbish{name='灯泡', isHarm=true, isRecycled=false, isDry=false, isWet=false}

可回收垃圾:
Rubbish{name='废纸塑料', isHarm=false, isRecycled=true, isDry=false, isWet=false}
Rubbish{name='金属和布料', isHarm=false, isRecycled=true, isDry=false, isWet=false}
Rubbish{name='玻璃', isHarm=false, isRecycled=true, isDry=false, isWet=false}

这样过滤器模式的简单应用就一目了然了吧

总结

总结下过滤器模式的特点:

  • 可插拔:过滤器的设计概念要求其是支持可插拔设计的;
  • 有序性:过滤器是被设计为一组组的过滤装置,要实现数据过滤,就必须有顺序性要求,比如我们要设计编解码过滤器,用户请求过来的 xml 数据会优先通过 xml2json 过滤器进行数据处理,完了再在响应发出前进行相应的 json2xml 过滤处理,以保证客户端交互以 xml 数据格式为准的同时系统内部数据交互还是维持 json 格式不变;
  • 过滤器的独立性:每种过滤器必须是独立的实体,其状态不受其它过滤器的影响,每个过滤器都有自己独立的数据输入输出接口,只要各个过滤器之间传送的数据遵守共同的规约就可以相连接。


Author: Re:0
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Re:0 !
 Previous
Redis + Lua脚本实现服务限流 Redis + Lua脚本实现服务限流
保障服务稳定的三大利器:熔断降级、服务限流和故障模拟。今天和大家谈谈限流算法的几种实现方式,本文所说的限流并非是Nginx层面的限流,而是业务代码中的逻辑限流。
2022-03-09
Next 
桥接模式 桥接模式
定义桥接模式 (Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体 (Handle and Body) 模式或接口 (Interface) 模式。
2022-03-08
  TOC