Dynamically extend Java applications - JavaWorld August 2001
Tutorial Details:
Dynamically extend Java applications
Dynamically extend Java applications
By: By Bill W. Davis
Use interfaces and dynamic class loading for added functionality
ave you ever developed a program you knew would require new functionality throughout its lifetime? Requirements state that the marketing department will make all sorts of pricing deals for every customer. Your program needs to handle those new requirements as they come along, or you must enable users to customize your software without the need to change source code.
Can you avoid changing the code you have already written and tested to add new functionality? Can you add new classes to your program without having to recompile the whole thing? The answer to both questions is yes, and as you might have guessed, is based on interfaces and dynamic class loading.
As an aside, most of the classes and architecture used in this article are simplified from their use in professional programming. The overall code demonstrates how to use interfaces to dynamically extend programs; it is not intended for use in real systems.
What are interfaces?
An interface simply describes the way an object is called. When you define an interface, you relate how other objects will use a specific object.
Most of you working with Java should already understand interfaces, as it is difficult to use Java without that knowledge. But for those of you unclear on the subject, I'll start at the beginning and then create more complicated examples. If you already understand interfaces, you can probably skim until we get to the Using Strings to Specify Class Name section.
The power of interfaces
The first example illustrates the power of interfaces. Assume your client is a brokerage house, and they want you to set up their trading system. They trade in all sorts of financial instruments: stocks, bonds, commodities, and so on. Different customers are charged different amounts for their trades; the amounts are defined by what the client calls pricing plans .
First, you think about the design of your classes. The main classes and their properties, defined by the client, may be as follows:
Customer : Name , Address , Phone , PricingPlan
Trade : TradeType (stock, bond, commodity), ItemTraded (stock symbol), NumberOfItemsTraded , ItemPrice , CommissionAmount
PricingPlan : Provides a call procedure to calculate the CommissionAmount for a trade
Coding without interfaces
You can code the pricing plans without using an interface and then enhance the code from there. Right now, the client has two pricing plans defined as follows:
Plan 1: $20/trade for regular customers
Plan 2: $15/trade for the first 10 trades in a month; $10/trade after that
The Trade object uses a PricingPlan object to calculate how much commission to charge the customer. You create a PricingPlan class for each pricing plan. The class for Plan 1 is called PricingPlan20 and Plan 2's class is called PricingPlan1510 . Both classes calculate the commission to charge using a procedure called CalcCommission() . The code looks like the following:
Class Name: PricingPlan20
public double calculateCommission( Trade trade )
{
return 20.0;
}
Class Name: PricingPlan1510
public double calculateCommission( Trade trade )
{
double commission = 0.0;
if( trade.getCustomer().getNumberOfTradesThisMonth() <= 10 )
commission = 15.0;
else
commission = 10.0;
return commission;
}
Here's the code to get the commission in Trade :
public double getCommissionPrice()
{
double commissionPrice = 0.0;
if( getCustomer().getPlanId() == 1 )
{
PricingPlan20 plan1 = new PricingPlan20();
commissionPrice = plan1.calculateCommission( this.getCustomer() );
plan1 = null;
}
else
{
PricingPlan1510 plan2 = new PricingPlan1510();
commissionPrice = plan2.calculateCommission( this.getCustomer() );
plan2 = null;
}
return commissionPrice;
}
Hardcoded interfaces
How can an interface make your life easier in the previous example? You can create the PricingPlan interface that the PricingPlan classes implement:
Interface Name: IPricingPlan
public interface IPricingPlan {
public double calculateCommission( Trade trade );
}
Since you are defining an interface, you don't define a body for the calculateCommission() method. The actual PricingPlan classes will fill in the code. The first modification you make to the PricingPlan classes is to declare that you will implement the interface you just defined. You do this by adding the following code to the top of the PricingPlan class definitions:
public class PricingPlan20 extends Object implements IPricingPlan {
When you declare that you will implement an interface in Java, you must provide bodies for all the methods that the interface defines (unless you are creating abstract classes, which is unrelated to this topic). So any class that implements the IPricingPlan interface must define a method called calculateCommission() . The method signature must be defined exactly the way the interface defines it, so it must accept a Trade object. Since we already had calculateCommission() methods in both PricingPlan classes, we won't modify them any further. If you create new PricingPlan classes, they must implement the IPricingPlan interface and the calculateCommission() method.
Next you can change the Trade class's getCommissionPrice() method to use the interfaces as follows:
Class Name: Trade
public double getCommissionPrice()
{
double commissionPrice = 0.0;
IPricingPlan plan;
if( getCustomer().getPlanId() == 1 )
{
plan = new PricingPlan20();
}
else
{
plan = new PricingPlan1510();
}
commissionPrice = plan.calculateCommission( this );
return commissionPrice;
}
Notice how you define the PricingPlan variable as an IPricingPlan interface. The instance you actually create depends on the customer's pricing plan. Since both PricingPlan classes implement the IPricingPlan interface, you can set the variable equal to a new instance of either one. Java, in general, doesn't care about the actual object that implements the interface, only about the interface itself.
Using strings to specify class name
Say your boss comes to you and says that the company just approved two new pricing plans, with more coming. The pricing plans are a flat rate of $8 and $10 per trade. You decide to create two new PricingPlan classes: PricingPlan8 and PricingPlan10 .
In that case, you must change the Trade class to include these new PricingPlans . You could simply add more if/then/else clauses, but that would get unwieldy as the number of pricing plans grows. Another option is to use the Class.forName() method instead of new when creating PricingPlan instances. Class.forName() lets you create instances using a string to name the class instead of hardcoding the name in the program. Here is an example of how you could use this in the Trade class's earlier code:
Class Name: Trade
public double getCommissionPrice()
{
double commissionPrice = 0.0;
IPricingPlan plan;
Class commissionClass;
try
{
if( getCustomer().getPlanId() == 1 )
{
commissionClass = Class.forName( "string_interfaces.PricingPlan20" );
}
else
{
commissionClass = Class.forName( "string_interfaces.PricingPlan1510" );
}
plan = (IPricingPlan) commissionClass.newInstance();
commissionPrice = plan.calculateCommission( this );
}
// ClassNotFoundException, InstantiationException, IllegalAccessException
catch( Exception e )
{
System.out.println( "Exception occurred: " + e.getMessage() );
e.printStackTrace();
}
return commissionPrice;
}
This code doesn't offer much of an advantage over the previous code. It's actually longer considering you must now include the exception-catching code. Look at what happens if you create an array of PricingPlan class names in the Trade class:
Class Name: Trade
public class Tradeextends Object {
private Customer customer;
private static final String[]
pricingPlans = { "string_interfaces.PricingPlan20",
"string_interfaces.PricingPlan1510",
"string_interfaces.PricingPlan8",
"string_interfaces.PricingPlan10"
};
Now you can change the getCommissionPrice() method to the following:
Class Name: Trade
public double getCommissionPrice()
{
double commissionPrice = 0.0;
IPricingPlan plan;
Class commissionClass;
try
{
commissionClass =
Class.forName( pricingPlans[ getCustomer().getPlanId() - 1 ] );
plan = (IPricingPlan) commissionClass.newInstance();
commissionPrice = plan.calculateCommission( this );
}
// ClassNotFoundException, InstantiationException, IllegalAccessException
catch( Exception e )
{
System.out.println( "Exception occurred: " + e.getMessage() );
e.printStackTrace();
}
return commissionPrice;
}
If you don't count the exception handling, this code is by far the simplest we've seen. It is also relatively easy to add new pricing plans. You simply create and add the plans to the Trade class's array.
I hope you begin to see the power available with dynamic class loading.
Now I'll switch gears and refactor my design for cleaner code. (See the Resources section for more information on refactoring.)
You can improve this design to make adding new pricing plans even easier. You still have to recompile the source file that contains the Trade class whenever a new pricing plan is added.
Database/XML-based class names
Imagine what would happen if you stored the class names in a database table, XML file, or even a plain text file. You could then add pricing plans by simply creating a new class, placing it where the program can find it, and adding a record to the database table or file. Then you wouldn't need to change the Trade class every time a new pricing plan was introduced. I will use a plain text file, since that is the easiest to code and makes for a simpler example. In a real system, I would certainly recommend either a database or XML file, since they allow more flexibility. The text file looks like the following:
File Name: PricingPlans.txt
1,string_interfaces.Pr
Read
Tutorial at: Click here to view the tutorial
Rate Tutorial: Dynamically extend Java applications - JavaWorld August 2001
View Tutorial: Dynamically extend Java applications - JavaWorld August 2001
Related
Tutorials:
XML messaging, Part
3
XML messaging, Part
3 |
JSP best practices
Follow these tips for reusable and easily maintainable JavaServer Pages |
Dynamically extend Java applications
Dynamically extend Java applications |
XSLT blooms with
Java
XSLT blooms with
Java |
Java security evolution
and concepts, Part 5
Java security evolution
and concepts, Part 5 |
Generate JavaBean classes dynamically with XSLT
Generate JavaBean classes dynamically with XSLT |
Use Web services
to integrate Web applications with
EISs
Use Web services
to integrate Web applications with
EISs |
Check out three
collections libraries
Check out three
collections libraries |
Sun boosts
Sun boosts enterprise Java |
Test email components in your software
Test email components in your software |
Excellent
tutorial on Struts and Tiles
Excellent tutorial on Struts and Tiles
This tutorial assumes knowledge of Java, JDBC, Servlets, J2EE (with regards to Web applications) and JSP Struts in a holistic manner, minus the beads and crystals.
The Tiles framework makes creating reusable pages |
Jappo - an open and modular Java preprocessor
Jappo - an open and modular Java preprocessor
Jappo is Java preprocessor. It examines input files for preprocessor statements (i.e. macros) that are then interpreted, resulting in the alteration of the input content that is stored as target file. Jappo |
Jython
Get to know Jython, in this first article in a new series introducing alternate languages for the Java Runtime Environment, alt.lang.jre. Jython is an implementation of the popular scripting language Python, but running on a JVM. For Python developers Jyt |
Clean Up Your Mess: Managing Temp Files in Java Apps
Clean Up Your Mess: Managing Temp Files in Java Apps
Creating and managing temporary files in a Java application can be a little tricky due to some open JVM bugs. Develop a workaround with some custom code and a clever design. |
JFormula 2.9 - Math expression API
JFormula 2.9 - Math expression API
JFormula is a Java library for evaluating various expressions (boolean, math, if/then/else...).
A lot of companies chose JFormula like EADS Space Transportation. |
Dynamic Delegation and Its Applications
Dynamic Delegation and Its Applications
The Proxy pattern is an important and widely used design pattern in object-oriented programming. Do you ever use Proxy in Java since its introduction in JDK 1.3? A dynamic proxy class is a class that implements a l |
Java Server Pages Dynamically Generated Web Content.
JavaServer PagesTM (JSP TM) technology allows Web developers and designers to rapidly develop and easily maintain, information-rich, dynamic Web pages that leverage existing business systems. |
Understanding Network Class Loaders Class loaders
One of the cornerstones of Java dynamics, determine when and how classes can be added to a running Java environment. |
Just Forms PDF library
The JustFormsPDF library is a Java class library for filling or editing interactive PDF forms on-the-fly. Empower your applications with the industry-standard PDF forms technology using JustFormsPDF. |
Getting Started With the PIM APIs
This article provides a code-intensive introductory tutorial to Personal information management (PIM) APIs, JSR 75. |
|
|
|