Browse DevX
Sign up for e-mail newsletters from DevX


Extend the JDK Classes with Jakarta Commons, Part I : Page 5

Explore the components in the Jakarta Commons set of reusable classes and you'll be convinced that most of them should be part of the JDK. Learn which ones you should use in your projects.




Building the Right Environment to Support AI, Machine Learning and Deep Learning


The purpose of the Functors API is to model procedural program elements as objects. For example, you might have written code that evaluates the objects in a Vector or ArrayList for some condition and takes action based on its result. You might have written if-elseif-elseif-else or switch-case statements. Functors let you model the conditions like if-else or switch-case and the expressions within these conditions as separate objects. Why do you need this? Some of the benefits include the following:
  • The design becomes modular, and the features can be extended or modified easily. You can use strategy and command patterns together to switch algorithms in an operation for a condition.
  • It simplifies writing unit test cases.
  • It allows dynamic loading of the operation for a condition using reflection.

Technically, most of the program elements (if, while, for, switch, etc.) can be modeled using functors. The common use of this concept is for series of if-else statements, switch-case statements, and operations to perform on a collection.

Important interfaces in this API are Predicate and Closure. A Predicate is used to represent a condition. If the result is true, then the Closure's execute method will be called.

The Functors example is a simple script-processing engine that processes database commands. It uses functors to model the switch-case decision tree, which decides which command class should be invoked for a script command. You'll find the complete source code in the in.co.narayanan.commons.collections.functors package. The following listing provides the command syntax:

add <tablename>, <column values> delete <tablename>, <key_column>, <value> modify <tablename>, <key_column>, <value>, <column name and new values>

The script engine will need to execute a SQL insert to the database when it encounters an add script command. Similarly, other commands have to be processed as well. The class ScriptCommand is the domain object, which represents a script command and its arguments. The classes AddTask, DeleteTask, and ModifyTask contain the logic to execute for a script command. To understand the traditional way for modeling this engine, review the following code snippet from the class ProcessCommand1:

public void process(ScriptCommand command) { switch(command.getType()) { case ADD: { executeAdd(command); } break; case MODIFY: { executeModify(command); } break; case DELETE: { executeDelete(command); } break; } }

It checks the script command type in a switch-case block and executes the appropriate command.

The following listing from ProcessCommand2 illustrates the same behavior achieved using functors:

public void start() { Map predicatesAndClosures = new Flat3Map(); predicatesAndClosures.put(new AddPredicate(), new AddClosure()); predicatesAndClosures.put(new ModifyPredicate(), new ModifyClosure()); predicatesAndClosures.put(new DeletePredicate(), new DeleteClosure()); Closure switchClosure = ClosureUtils.switchClosure(predicatesAndClosures); CollectionUtils.forAllDo(commands, switchClosure); } private static class AddPredicate implements Predicate { public boolean evaluate(Object object) { ScriptCommand command = (ScriptCommand)object; return command.getType() == CommandType.ADD ? true : false; } } private static class AddClosure implements Closure { public void execute(Object input) { ScriptCommand command = (ScriptCommand)input; AddTask add = new AddTask(); add.add(command.getTableName(), command.getArgs()); } }

The AddPredicate evaluates the command type, and the AddClosure contains the logic to perform the add script command. The predicates and closures are mapped and passed to the switchClosure method in the ClosureUtils utility class. The SwitchClosure reference returned evaluates the predicates and calls mapped to closure if the result is true. This is repeated for each predicate in the map until one returns true.

This example shows that closures can be used together to achieve program flow. It uses AddClosure, which was written, and SwitchClosure in the Commons Collections API together. The SwitchClosure reference can be used with IfClosure, for instance.

A very common use of functors in a business application is applying a set of business rules to a collection of domain object instances and taking action with respect to the rule results. The rules need to be modeled as predicates and the actions need to be modeled as closures. You can build a complete rule engine using the Python scripting language if you integrate it to Jython. In this case, the Python scripts need to be used for writing the predicates and closures.

Thanks for your registration, follow us on our social networks to keep up-to-date