Backend Development 8 min read

Design and Implementation of a Java Rule Engine with AND/OR Logic

This article explains how to design and refactor a Java rule engine that supports short‑circuit AND/OR logic, demonstrates the abstraction of rules, shows concrete implementations, and provides a chainable service for executing combined rule sets with example code.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Design and Implementation of a Java Rule Engine with AND/OR Logic

The author presents a practical case of extending an existing user‑application rule system by building a reusable rule engine in Java, emphasizing the need for short‑circuit evaluation of AND/OR conditions and highlighting maintainability concerns of ad‑hoc if‑else chains.

Initial business logic is illustrated with a simple if‑else snippet:

if (是否海外用户) {
    return false;
}

if (刷单用户) {
    return false;
}

if (未付费用户 && 不再服务时段) {
    return false;
}

if (转介绍用户 || 付费用户 || 内推用户) {
    return true;
}

The engine’s design introduces a data transfer object ( RuleDto ), a BaseRule interface, an abstract AbstractRule template, concrete rule classes ( AddressRule , NationalityRule ), and a constants holder ( RuleConstant ).

// 业务数据
@Data
public class RuleDto {
    private String address;
    private int age;
}

// 规则抽象
public interface BaseRule {
    boolean execute(RuleDto dto);
}

// 规则模板
public abstract class AbstractRule implements BaseRule {
    protected
T convert(RuleDto dto) {
        return (T) dto;
    }
    @Override
    public boolean execute(RuleDto dto) {
        return executeRule(convert(dto));
    }
    protected
boolean executeRule(T t) {
        return true;
    }
}

// 具体规则-例子1
public class AddressRule extends AbstractRule {
    @Override
    public boolean execute(RuleDto dto) {
        System.out.println("AddressRule invoke!");
        if (dto.getAddress().startsWith(MATCH_ADDRESS_START)) {
            return true;
        }
        return false;
    }
}

// 具体规则-例子2
public class NationalityRule extends AbstractRule {
    @Override
    protected
T convert(RuleDto dto) {
        NationalityRuleDto nationalityRuleDto = new NationalityRuleDto();
        if (dto.getAddress().startsWith(MATCH_ADDRESS_START)) {
            nationalityRuleDto.setNationality(MATCH_NATIONALITY_START);
        }
        return (T) nationalityRuleDto;
    }
    @Override
    protected
boolean executeRule(T t) {
        System.out.println("NationalityRule invoke!");
        NationalityRuleDto nationalityRuleDto = (NationalityRuleDto) t;
        if (nationalityRuleDto.getNationality().startsWith(MATCH_NATIONALITY_START)) {
            return true;
        }
        return false;
    }
}

// 常量定义
public class RuleConstant {
    public static final String MATCH_ADDRESS_START = "北京";
    public static final String MATCH_NATIONALITY_START = "中国";
}

The RuleService class builds the execution flow, storing rule lists keyed by AND (1) or OR (0), and provides and() , or() , and execute() methods that iterate through the rules applying short‑circuit logic.

public class RuleService {
    private Map
> hashMap = new HashMap<>();
    private static final int AND = 1;
    private static final int OR = 0;

    public static RuleService create() { return new RuleService(); }
    public RuleService and(List
ruleList) { hashMap.put(AND, ruleList); return this; }
    public RuleService or(List
ruleList) { hashMap.put(OR, ruleList); return this; }
    public boolean execute(RuleDto dto) {
        for (Map.Entry
> item : hashMap.entrySet()) {
            List
ruleList = item.getValue();
            switch (item.getKey()) {
                case AND:
                    System.out.println("execute key = " + 1);
                    if (!and(dto, ruleList)) return false;
                    break;
                case OR:
                    System.out.println("execute key = " + 0);
                    if (!or(dto, ruleList)) return false;
                    break;
                default:
                    break;
            }
        }
        return true;
    }
    private boolean and(RuleDto dto, List
ruleList) {
        for (BaseRule rule : ruleList) {
            if (!rule.execute(dto)) return false;
        }
        return true;
    }
    private boolean or(RuleDto dto, List
ruleList) {
        for (BaseRule rule : ruleList) {
            if (rule.execute(dto)) return true;
        }
        return false;
    }
}

A test class demonstrates chainable construction and execution of the rule engine, combining nationality, name, and address rules with AND logic and age/subject rules with OR logic.

public class RuleServiceTest {
    @org.junit.Test
    public void execute() {
        // define rules
        AgeRule ageRule = new AgeRule();
        NameRule nameRule = new NameRule();
        NationalityRule nationalityRule = new NationalityRule();
        AddressRule addressRule = new AddressRule();
        SubjectRule subjectRule = new SubjectRule();
        // create DTO
        RuleDto dto = new RuleDto();
        dto.setAge(5);
        dto.setName("张三");
        dto.setAddress("北京");
        dto.setSubject("数学");
        // chain and execute
        boolean ruleResult = RuleService
                .create()
                .and(Arrays.asList(nationalityRule, nameRule, addressRule))
                .or(Arrays.asList(ageRule, subjectRule))
                .execute(dto);
        System.out.println("this student rule execute result :" + ruleResult);
    }
}

The author concludes that the engine is simple and modular, allowing independent rule development, but notes the drawback of shared DTO data coupling, recommending pre‑construction of required data to improve design.

backendDesign Patternsjavarule engineCode RefactoringAND/OR Logic
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.