Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
305: Practice Exercise: Building a Simple Rules Engine
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
UserorOrder) 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
Customerclass with fields fortotalSpend(double) andyearsAsMember(int). - Create a
LoyaltyRuleinterface with two methods:boolean matches(Customer c)andString 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
LoyaltyEngineclass that holds a list of these rules. ThecalculateTier(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".
There are no comments for now.