Design and Implementation of a Java Rule Engine

This article presents a Java-based rule engine design, illustrating rule abstraction, concrete rule implementations, and a flexible executor supporting AND/OR logic, accompanied by code examples and a discussion of its advantages and drawbacks for maintainability.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Design and Implementation of a Java Rule Engine

The author introduces a requirement to extend trial user application rules and outlines the logical conditions using simple if statements.

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

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

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

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

He then explains that the main process relies on logical AND/OR relationships and the need for short‑circuit evaluation.

To improve maintainability, he proposes refactoring the rule processing by defining a rule abstraction, concrete rule classes, and a rule executor that can combine rules with AND/OR semantics.

The rule abstraction consists of a BaseRule interface with an execute method, an AbstractRule class providing conversion and execution templates, and specific rule implementations such as AddressRule and NationalityRule.

// Business data
@Data
public class RuleDto {
    private String address;
    private int age;
}

// Rule abstraction
public interface BaseRule {
    boolean execute(RuleDto dto);
}

// Rule template
public abstract class AbstractRule implements BaseRule {
    protected <T> T convert(RuleDto dto) { return (T) dto; }
    @Override
    public boolean execute(RuleDto dto) {
        return executeRule(convert(dto));
    }
    protected <T> boolean executeRule(T t) { return true; }
}

// Concrete rule example
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;
    }
}

// Another concrete rule example
public class NationalityRule extends AbstractRule {
    @Override
    protected <T> T convert(RuleDto dto) {
        NationalityRuleDto nrDto = new NationalityRuleDto();
        if (dto.getAddress().startsWith(MATCH_ADDRESS_START)) {
            nrDto.setNationality(MATCH_NATIONALITY_START);
        }
        return (T) nrDto;
    }
    @Override
    protected <T> boolean executeRule(T t) {
        System.out.println("NationalityRule invoke!");
        NationalityRuleDto nrDto = (NationalityRuleDto) t;
        if (nrDto.getNationality().startsWith(MATCH_NATIONALITY_START)) {
            return true;
        }
        return false;
    }
}

// Constants
public class RuleConstant {
    public static final String MATCH_ADDRESS_START = "北京";
    public static final String MATCH_NATIONALITY_START = "中国";
}

The RuleService class builds a map of rule lists keyed by AND (1) or OR (0) and provides chainable methods and() and or() to register rule groups, then executes them with short‑circuit logic.

public class RuleService {
    private Map<Integer, List<BaseRule>> 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<BaseRule> ruleList) { hashMap.put(AND, ruleList); return this; }
    public RuleService or(List<BaseRule> ruleList) { hashMap.put(OR, ruleList); return this; }

    public boolean execute(RuleDto dto) {
        for (Map.Entry<Integer, List<BaseRule>> item : hashMap.entrySet()) {
            List<BaseRule> ruleList = item.getValue();
            switch (item.getKey()) {
                case AND:
                    if (!and(dto, ruleList)) return false;
                    break;
                case OR:
                    if (!or(dto, ruleList)) return false;
                    break;
                default:
                    break;
            }
        }
        return true;
    }

    private boolean and(RuleDto dto, List<BaseRule> ruleList) {
        for (BaseRule rule : ruleList) {
            if (!rule.execute(dto)) return false;
        }
        return true;
    }

    private boolean or(RuleDto dto, List<BaseRule> ruleList) {
        for (BaseRule rule : ruleList) {
            if (rule.execute(dto)) return true;
        }
        return false;
    }
}

A test class demonstrates how to instantiate concrete rules, create a DTO with sample data, and execute the combined rule engine using a fluent API.

public class RuleServiceTest {
    @Test
    public void execute() {
        AgeRule ageRule = new AgeRule();
        NameRule nameRule = new NameRule();
        NationalityRule nationalityRule = new NationalityRule();
        AddressRule addressRule = new AddressRule();
        SubjectRule subjectRule = new SubjectRule();

        RuleDto dto = new RuleDto();
        dto.setAge(5);
        dto.setName("张三");
        dto.setAddress("北京");
        dto.setSubject("数学");

        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);
    }
}

Finally, the author summarizes the pros—simplicity, independent rules, clear separation of data, rule, and executor—and cons—tight coupling through a shared DTO, suggesting pre‑building data to improve design.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsJavarule engineBackend DevelopmentCode Example
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

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.