Skip to Content
Course content

305: Practice Exercise: Building a Simple Rules Engine

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're a customs officer at an international airport. You don't just have one giant, complicated mental checklist for every single person who walks through your gate. Instead, you have a series of distinct checks: Do they have a valid passport? Is their visa current? Are they carrying prohibited items? If any one of these "rules" fails, the person is flagged. If they pass all of them, they're clear to enter.

In Java, we often fall into the trap of writing a "giant mental checklist" using a massive chain of if-else if-else statements. It works fine when you have three rules, but when your business logic grows to thirty rules, that method becomes a nightmare to read, test, or change without breaking something unrelated. That's why we build a Rules Engine. We stop treating logic as a sequence of statements and start treating it as a collection of objects.

Mapping the Airport to the Code

To move from a hard-coded mess to a rules engine, we map the customs process directly into our architecture:

  • The Traveler is our Fact. This is the data object (like a User or Order) that we are evaluating.
  • The Checklist is our Rules Engine. This is the class that holds a list of rules and iterates through them.
  • The Individual Requirement is the Rule. This is a standalone object with a single responsibility: deciding if the Fact meets a specific condition.

Turning Logic into Objects

The secret here is the Interface. I always start by defining what a "Rule" actually looks like. In a simple engine, a rule just needs to know if it applies to the current data and what action to take if it does. Let's look at a scenario where we're calculating shipping discounts for an e-commerce store.

public interface ShippingRule {
    boolean isApplicable(Order order);
    void apply(Order order);
}

Now, instead of one giant method, I can create small, focused classes. One class handles "Free shipping for orders over $100," and another handles "Discount for Gold members." I've seen this save teams dozens of hours in debugging because when the "Gold member" logic breaks, you know exactly which tiny class to look at, rather than hunting through a 500-line ShippingService.

Orchestrating the Evaluation

Once we have our rules, we need the "Officer" to run them. The engine is essentially just a wrapper around a List of rules. I prefer using a list because it allows us to control the order of execution—which is critical if one rule's action changes the data that a subsequent rule evaluates.

public class ShippingRulesEngine {
    private final List<ShippingRule> rules = new ArrayList<>();

    public void addRule(ShippingRule rule) {
        rules.add(rule);
    }

    public void process(Order order) {
        for (ShippingRule rule : rules) {
            if (rule.isApplicable(order)) {
                rule.apply(order);
            }
        }
    }
}

By structuring it this way, the ShippingRulesEngine doesn't actually know what the rules are; it only knows how to run them. This is the Open/Closed Principle in action: the engine is closed for modification but open for extension. Need a new "Black Friday" discount? Just write a new BlackFridayRule class and plug it into the list. You don't have to touch the engine's code at all.




📋 Practical Task

Build a Loyalty Tier Calculator

Your task is to implement a simple rules engine that determines a customer's loyalty tier based on their account history. You need to move away from if/else blocks and implement a pluggable system.

Requirements:

  • Create a Customer class with fields for totalSpend (double) and yearsAsMember (int).
  • Create a LoyaltyRule interface with two methods: boolean matches(Customer c) and String getTier().
  • Implement three specific rules:
    • PlatinumRule: Spend > 10,000 AND members for 5+ years.
    • GoldRule: Spend > 5,000 OR members for 2+ years.
    • SilverRule: Spend > 1,000.
  • Create a LoyaltyEngine class that holds a list of these rules. The calculateTier(Customer c) method should return the tier of the first rule that matches (since the most prestigious tier should be checked first).

Testing your code: Create a Main class, instantiate the engine, add the rules in order of priority (Platinum, then Gold, then Silver), and test it with a customer who has spent $6,000 and been a member for 1 year. They should be flagged as "Gold".

Rating
0 0

There are no comments for now.

to be the first to leave a comment.