Command pattern reference implementation. Custom implementation for a rules engine like execution.
- Java 8 - it'll very likely work with earlier versions, it's just that we haven't tested it.
- Maven 3 or later
>mvn clean install
>mvn exec:java
This implementation is customized to execute commands in a "rules" like implementation with 3 different execution stages:
- Invoker - returns a
List<Evaluator>where the parent abstract class iterates and firesexecute()on each Evaluator item in the list. Its main job is to invoke Evaluators (below). - Evaluator - returns a
List<Command>where the parent abstract class iterates and firesexecute()for each Command in the list. Its main job is to place conditionals before each command is executed. For example, it won't add a command to theList<Command>to be executed because the concrete class condition returned false. This is a great way to place "ifs" statements outside the commands themselves (below). - Command - the concret command class implements an
execute()method which is called by the Evaluator. When called, it will always execute, leaving conditions to be checked by the Evaluator.
- It eliminates or significantly reduces procedural code.
- Increases cohesion (well defined classes) and eliminates tight coupling.
- Increases testability.
- Improves readability, maintenability, and extensibility. It makes it easier to innovate and improve your code base without fearing breaking something else (high cohesion, low coupling).
- Context driven execution. The context is clearly defined and returned at the end of the last invoker, with all populated values.
- Rules engine
- Chained calculations (e.g. insurance quotes, inventory re-ordering, credit limits, etc.)
- Plug-in like implementations (e.g. each command can call a separate plug-in).
- Many more, these are just examples
The context starts with someCalculationResult = 0 then at the end of the process the result is 1200. This is a simple way to show how calculation rules are invoked, evaluated, and executed. The result is a correctly populated context.
Note: the terms CreditUsage, NormalizedCreditLimit, etc. are just generic examples with no real life application in this example.
The following is what the console displays when running the code (approximate):
Main process started.
STEP 1. Simulating data retrieval from the DB...
..added DB data into context.
..currency Code from DB: USD
..someCalculationResult (should be 0): 0
STEP 2. Calling calculation engine with context
..Beginning Invoker loop.
..Executing invoker. Name: CalculateCreditUsage
..Beginning Invoker loop.
..Executing evaluator. Name: CreditUsage
....Beginning Evaluator loop...
......Executing command: NormalizedCreditLimit
........Validating Normalized Credit Limit
........Executing Normalized Credit Limit
......Command execution completed. Name: NormalizedCreditLimit
....Number of commands executed in Evaluator loop: 1
....Evaluator execution completed. Status: Successful
..Execution completed. Name: CreditUsage
..Number of evaluations in Invoker loop: 1
..Invoker execution completed. Status: Successful
..Execution completed. Name: CalculateCreditUsage
..Number of evaluations in Invoker loop: 1
..Invoker execution completed. Status: Successful
STEP 3. Simulating saving results to the DB
..currency Code: USD
..someCalculationResult: 1200
- Create unit tests
- More concrete examples.