二十三种设计模式(二十)--解释器模式

解释器模式(Interpret)

当我们需要在程序运行时, 通过配置筛选形成不同的规则, 从而让程序根据动态变化的规则得到我们期待的结果时, 并且这些规则本身比较通用, 只需要在程序运行时, 通过用户自行组合配置来实现某一个功能时, 就需要解释器模式.

简单讲, 解释器模式用来解决基本规则众多且固定, 运行中规则组合多变的场景

经典的解释器模式如计算器, 加, 减, 乘, 除, 百分比等这些基础规则, 去解决我们复杂的组合运算, 各种加减乘除运算组合嵌套.

当然, 实际生产中我们不会去写计算器这么无意义的项目.

现实场景中, 想象我们要开发一个电话机器人, 它每天负责根据我们提供的电话列表给海量的用户打电话, 我们在后台要建立一套用户筛选机制, 这个机制包括以下几项内容:

  1. 用户的咨询意向等级: ABCDE五个等级
  2. 年龄分段: 10-20, 20-30, 30-40, 40-50, 50+
  3. 性别: 男, 女
  4. 用户当时的心理咨询状态: 焦虑, 抑郁, 双向情感障碍, 安全型

所有的客户类型都会保存入数据库, 但是, 我们会根据如下条件组合快速地把符合我们期待的意向客户推送给客服人员去快速对接. 具体需求如下:

有时我需要筛选出20-30岁的男性抑郁状态有A级咨询意向的客户

有时我又需要找出焦虑的50+岁女性在C级以上的咨询意向的客户

针对这些需求, 我们组合条件时需要将条件用"或", "等于", "且"这些基本的操作规则连接起来, 实现最终的筛选

如下是代码实现:

先定义客户类, 包含客户各项属性, 这里用直接构造的方式初始化各项属性, 实际开发过程中会使用哈希表来动态地增删各项属性

java 复制代码
enum Sex {
    MAN,
    WOMEN
}

enum Age {
    AGE_10(10),
    AGE_20(20),
    AGE_30(30),
    AGE_40(40),
    AGE_50(50);

    private final int age;

    Age(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

enum MentalState {
    ANXIOUS,
    DEPRESSION,
    SAFE,
    BIPOLAR_DISORDER
}

enum DesireLevel {
    A, B, C, D, E
}

// 记录客户分类信息, 解释器将根据这些信息组合条件确定是否推送
class Customer {
    private  final Sex sex;
    private  final int age;
    private  final MentalState mentalState;
    private  final DesireLevel desireLevel;

    Customer(Sex sex, int age, MentalState state, DesireLevel level) {
        this.sex = sex;
        this.age = age;
        this.mentalState = state;
        this.desireLevel = level;
    }

    public Sex getSex() {
        return sex;
    }

    public int getAge() {
        return age;
    }

    public MentalState getMentalState() {
        return mentalState;
    }

    public DesireLevel getDesireLevel() {
        return desireLevel;
    }
}

接下来定义解释器通用接口, 及各种原子解释器, 这里也是解释器模式的核心

在解释器实现的过程中, 需要了解Java函数式接口, 从而可以实现通配Customer中的getter方法,

避免每一个属性对应一个解释器

java 复制代码
// 最基础的解释器统一接口
interface Expression {
    boolean interpret(Customer customer);
}


// 相等关系解释器, 判断Customer对象中某个值是否等于期待的值
// 通过函数式接口通配Customer中的方法
class Equals<T> implements Expression {
    private Function<Customer, T> extractor;
    private T expected;
    Equals(Function<Customer, T> extractor, T expected) {
        this.extractor = extractor;
        this.expected = expected;
    }

    @Override
    public boolean interpret(Customer customer) {
        return expected.equals(extractor.apply(customer));
    }
}

// 逻辑与, 这里判断的是多个解释器只要有一个不成立, 整体就不成立
class And implements Expression {
    List<Expression> expressionList;

    And(List<Expression> expressionList) {
        this.expressionList = expressionList;
    }

    @Override
    public boolean interpret(Customer customer) {
        for (Expression expression : expressionList) {
            if (!expression.interpret(customer)) {
                return false;
            }
        }

        return true;
    }
}

// 逻辑或, 这里同样, 判断的是多个解释器, 只要有一个成立, 整体就成立
class Or implements Expression {
    List<Expression> expressionList;

    Or(List<Expression> expressionList) {
        this.expressionList = expressionList;
    }

    @Override
    public boolean interpret(Customer customer) {
        for (Expression expression: expressionList) {
            if (expression.interpret(customer)) {
                return true;
            }
        }
        return false;
    }
}

// 解释器规则可以灵活扩展
// 下面扩展一个"区间"解释器
class Between implements Expression {
    ToIntFunction<Customer> extractor;
    int min;
    int max;

    Between(ToIntFunction<Customer> extractor, int min, int max) {
        this.extractor = extractor;
        this.min = min;
        this.max = max;
    }

    @Override
    public boolean interpret(Customer customer) {
        int val = extractor.applyAsInt(customer);
        return val >= min && val <= max;
    }
}

最后定义一个规则容器, 用来承接各种解释器规则拼接后的结果

以及一个规则过滤器, 用来利用生成的规则, 过滤客户信息, 生成最终结果

java 复制代码
// 解释器规则自由组合后, 需要一个类来存储规则, 生成具体的规则实例
class Rule {
    private final Expression expression;

    Rule(Expression expression) {
        this.expression = expression;
    }

    public boolean match(Customer customer) {
        return expression.interpret(customer);
    }
}

// 根据Rule规则, 过滤筛选用户
class RuleEngine {
    public void filter(List<Customer> customers, Rule rule) {
        for(Customer customer: customers) {
            if (rule.match(customer)) {
                System.out.println("性别: " + customer.getSex()
                                    + " 年龄: " + customer.getAge()
                                    + " 情感状态: " + customer.getMentalState()
                                    + " 咨询倾向等级: " + customer.getDesireLevel());
            }
        }
    }
}

以上就是整个解释器模式的实现, 在最终main方法调用展示解释器模式如何灵活地拼凑规则

java 复制代码
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.List;
import java.util.ArrayList;

public class InterpretPattern {
    public static void main(String[] args) {
        // 先创建5个Customer并加入列表中
        Customer c1 = new Customer(Sex.MAN, Age.AGE_20.getAge(), MentalState.ANXIOUS, DesireLevel.A);
        Customer c2 = new Customer(Sex.MAN, Age.AGE_30.getAge(), MentalState.SAFE, DesireLevel.B);
        Customer c3 = new Customer(Sex.WOMEN, Age.AGE_30.getAge(), MentalState.DEPRESSION, DesireLevel.B);
        Customer c4 = new Customer(Sex.WOMEN, Age.AGE_50.getAge(), MentalState.SAFE, DesireLevel.E);
        Customer c5 = new Customer(Sex.MAN, Age.AGE_40.getAge(), MentalState.BIPOLAR_DISORDER, DesireLevel.A);

        ArrayList<Customer> customers = new ArrayList<>();
        customers.add(c1);
        customers.add(c2);
        customers.add(c3);
        customers.add(c4);
        customers.add(c5);

        // 通过组合条件来实现动态的动态的筛选功能
        // 条件1: 筛选情绪状态为SAFE的女性客户
        Expression groupSafeWomen = new And(List.of(
                new Equals<>(Customer::getMentalState, MentalState.SAFE),
                new Equals<>(Customer::getSex, Sex.WOMEN)
        ));
        Rule rule01 = new Rule(groupSafeWomen);

        // 条件2: 筛选男性客户
        Expression groupMan = new Equals<>(Customer::getSex, Sex.MAN);
        Rule rule02 = new Rule(groupMan);

        // 条件3: 筛选20-40岁之间情绪状态为BIPOLAR_DISORDER或DEPRESSION的, 咨询意向在A或B的用户
        Expression group_20_40 = new And(List.of(
                new Between(Customer::getAge,
                        Age.AGE_20.getAge(), Age.AGE_40.getAge()),
                new Or(List.of(
                        new Equals<>(Customer::getMentalState, MentalState.BIPOLAR_DISORDER),
                        new Equals<>(Customer::getMentalState, MentalState.DEPRESSION)
                )),
                new Or(List.of(
                        new Equals<>(Customer::getDesireLevel, DesireLevel.A),
                        new Equals<>(Customer::getDesireLevel, DesireLevel.B)
                ))
        ));

        Rule rule03 = new Rule(group_20_40);

        // 传入列表, 分别验证以上三种筛选规则
        RuleEngine ruleEngine = new RuleEngine();
        System.out.println("===> 规则一: 情绪状态为SAFE的女性客户");
        ruleEngine.filter(customers, rule01);

        System.out.println("===> 规则二: 男性客户");
        ruleEngine.filter(customers, rule02);

        System.out.println("===> 规则三: 20-40岁之间情绪状态为BIPOLAR_DISORDER或DEPRESSION的, 咨询意向在A或B的用户");
        ruleEngine.filter(customers, rule03);
    }
}

运行后输出结果

复制代码
===> 规则一: 情绪状态为SAFE的女性客户
性别: WOMEN 年龄: 50 情感状态: SAFE 咨询倾向等级: E
===> 规则二: 男性客户
性别: MAN 年龄: 20 情感状态: ANXIOUS 咨询倾向等级: A
性别: MAN 年龄: 30 情感状态: SAFE 咨询倾向等级: B
性别: MAN 年龄: 40 情感状态: BIPOLAR_DISORDER 咨询倾向等级: A
===> 规则三: 20-40岁之间情绪状态为BIPOLAR_DISORDER或DEPRESSION的, 咨询意向在A或B的用户
性别: WOMEN 年龄: 30 情感状态: DEPRESSION 咨询倾向等级: B
性别: MAN 年龄: 40 情感状态: BIPOLAR_DISORDER 咨询倾向等级: A
相关推荐
网安_秋刀鱼1 天前
【java安全】反序列化 - CC1链
java·c语言·安全
零度@1 天前
Java消息中间件-Kafka全解(2026精简版)
java·kafka·c#·linq
钱多多_qdd1 天前
springboot注解(二)
java·spring boot·后端
Cosmoshhhyyy1 天前
《Effective Java》解读第32条:谨慎并用泛型和可变参数
java·python
攀登的牵牛花1 天前
前端向架构突围系列 - 架构方法(一):概述 4+1 视图模型
前端·设计模式·架构
帅气的你1 天前
面向Java程序员的思维链(CoT)提示词写法学习指南
java
雲墨款哥1 天前
从一行好奇的代码说起:React的 useEffect 到底是不是生命周期?
前端·react.js·设计模式
一只小小Java1 天前
Java面试场景高频题
java·开发语言·面试
沛沛老爹1 天前
Web开发者快速上手AI Agent:基于Function Calling的12306自动订票系统实战
java·人工智能·agent·web转型