Implement Design
by Contract for Java using dynamic proxies
Tutorial Details:
Implement Design by Contract for Java using dynamic proxies
Implement Design by Contract for Java using dynamic proxies
By: By Anders Eliasson
Write bug-free code with the DBCProxy framework
hile developing software and its subsequent versions, developers often enhance functionality by modifying existing code. However, developers who modify or enhance that code are usually not its original authors. Depending on the complexity of code and its supporting documentation, and the clarity of requirements, enhancements may present a difficult problem.
For instance, suppose you could enhance an existing application by performing the following tasks: extend a class and override a method in the extended class. Questions arising during the design and implementation phase would most likely include: Should the super class method be called? If the super class method is called, should the overriding method call the super class method at the beginning or end of the overriding method?
Many developers experience this situation everyday as they maintain and enhance existing software. As a result, they are at the mercy of requirements specifications and existing software documentation to understand software's complexity. If either artifact is too vague, the developer may find designing and implementing the necessary changes difficult.
However, software that includes assertions, based on the Design by Contract (DBC) theory, combats that problem. You could clearly design and subsequently enhance that software because those assertions would advertise class, method, and interface contractual obligations. The assertions would provide a clear enforceable picture of the existing software's state, behavior, and interface. Thus, they make enhancing existing software much easier for developers.
In this article, I present a DBC for Java framework called DBCProxy . This framework uses dynamic proxy classes , which were introduced in Sun Microsystems's JDK 1.3. I'll begin by briefly introducing DBC, and then I'll describe DBCProxy's architecture and how you can use it in your development process.
Introducing DBC
When software developers perform object-oriented analysis and design (OOAD), they strive to identify appropriate abstractions within a problem domain. Additionally, they label these abstractions with a specific name and usually provide some additional text to further clarify it. In the software construction and implementation phase, developers utilize Java to transform these abstractions into Java interfaces, classes, and methods. These constructs let you advertise an abstraction's purpose and functionality.
Descriptive text and identifying names may offer a great deal of information about an abstraction. However, rarely do they provide a precise specification. Descriptive text may contain enough information to make it sound like a detailed specification, but plain text is often ambiguous and not enforceable .
DBC provides a formal way of writing detailed specifications with assertions; these can be viewed as enforceable contracts between the designer/developer of a class or interface (abstraction) and the class or interface client. To accomplish this, DBC uses three assertion types:
Preconditions: An object's valid state and the arguments passed to a method prior to a method call
Postconditions: An object's valid state and the arguments passed to a method after a method call
Class invariants: An object's valid state
When looking at contractual responsibilities, the client must satisfy the precondition prior to invoking a method. The supplier must satisfy the postcondition and class invariant. This clarifies the functionality provided and establishes an enforceable contract between a client and a supplier. Consequently, when a contract breaks (a software bug exists), you can easily tell whether the client or supplier did not satisfy its assertion(s). Also important to understand, DBC assertions are not part of the implementation. Rather, they are part of a class or interface specification. DBC clearly separates specification (what) from implementation (how). You can find additional information on DBC in Resources .
DBC in the development process
As software grows larger and more complex, the need to identify architectures and designs that satisfy such requirements as scalability, availability, security, and so on proves more important to the software's overall success. On large software projects, a team of software architects and software designers commonly specify an API for a subsystem and subsequently hand it off to a developer team to implement. The API specification is often ambiguous; in some cases, the architect or designer may interpret the specification differently than the implementer, which may introduce defects. Since DBC is assertions-based, it can help in these situations. Additionally, DBC possesses the following advantages:
Assertions communicate specifications clearly and precisely.
Class methods and interfaces are specified with assertions so architecture and design require more thought.
When the implementation doesn't satisfy assertions, assertion exceptions are thrown during software testing.
API users can begin implementation since DBC-based specifications clearly and precisely communicate what the client must do in a precondition. In addition, the API specification communicates the API functionality in the form of a postcondition and invariants.
DBC applied to Java
As described above, DBC and assertions are not part of the implementation. Rather, they are part of a class or interface specification. How can we isolate DBC and assertions from the implementation in Java? Here's an example of how we might apply assertions in Java: Suppose method m(x) asserts that x is never null. In Java, the code may form:
void m(Object x)
{
assert(x != null);
}
In the above example, the assertion is part of the implementation; instead it should be part of the specification. In other words, the above example should form:
assert(x != null)
void m(Object x)
{
}
or
void m(Object x)
assert(x != null)
{
}
However, neither form will compile in Java. To effectively utilize DBC in Java, we must find an alternative mechanism, possibly Java's /** */ comment syntax. The special comments /** */ are most commonly used in front of class and method declarations. If you placed assertions inside these special comments, the Java compiler javac would ignore them.
You could also add assertions in Java by using tags. The documentation tool javadoc supports a variety of predefined tags. However, you can define additional tags that javadoc and its Doclet architecture can process.
In this article, I introduce three new tags to support assertions and DBC in Java. One for each assertion type:
@pre for preconditions
@post for postconditions
@invariant for invariants
The rules for these new tags are as follows:
You must follow all tags with a logical_statement , except when you use keywords $implies and $return (described later):
@pre logical_statement [, wait]
@post logical_statement
@invariant logical_statement
The logical_statement uses regular Java syntax. You use the text inside the logical_statement as-is (again, except when using $implies and $return ); in other words, it is not further parsed. Not supporting a separate DBC syntax was a clear design goal of the DBCProxy framework.
You must prefix method calls on a primary object with obj() . A primary object is the object for which the assertion is being made. For example, if a primary object's interface has a method called count() , then @pre obj().count() >= 0 is a valid precondition. To make this a valid postcondition or invariant, simply replace @pre with @post or @invariant .
To apply an assertion conditionally, you use the keyword $implies , which has the following syntax:
@pre logical_statement $implies logical_statement
@post logical_statement $implies logical_statement
@invariant logical_statement $implies logical_statement
In the following sections, I'll present rules and examples for the three assertion types, @pre , @post , and @invariant , in more detail.
Preconditions
You declare preconditions in a method's special comments section, as described above. Preconditions can assert arguments and call methods on the primary object. In the following example, the precondition asserts the object x passed to the put() method must not equal null :
/**
* @pre x != null
*/
public void put(Object x);
The next example asserts the primary object must not be empty and calls the method empty() on the primary object as part of the precondition declaration:
/**
* @pre !obj().empty()
*/
public Object item(Object x);
I describe the wait keyword, which optionally follows the logical_statement , in the section "Separate Objects."
Postconditions
You also declare postconditions in the method's special comments section. A postcondition has the same capabilities as a precondition but possesses two additional features:
It has access to the primary object's state before the method call by prefixing that call with old() . This example uses old() and asserts the method count() will increment by one, as a result of executing the put() method:
/**
* @post obj().count() == old().count() + 1
*/
public void put(Object x);
If a method has a return value, you use the keyword $return . This example uses $return and asserts the item() method will never return null :
/**
* @post $return != null
*/
public Object item();
Invariants
You declare invariants in the class/interface special comments. Invariants can only call methods on the primary object. This example asserts the method count() will always return a value greater than or equal to zero:
/**
* @invariant obj().count() >= 0
*/
public interface Stack
{
public int count();
}
To better illustrate how to use preconditions, postconditions, and invariants, Listing 1 shows a Stack interface wi
Read
Tutorial at: Click here to view the tutorial
Rate Tutorial: Implement Design
by Contract for Java using dynamic proxies
View Tutorial: Implement Design
by Contract for Java using dynamic proxies
Related
Tutorials:
Java Tip 56: How to
eliminate debugging problems for RMI-based applications - JavaWorld - July 1998
Java Tip 56: How to
eliminate debugging problems for RMI-based applications - JavaWorld - July 1998 |
Java Tip 68: Learn how to implement the Command pattern in Java - JavaWorld - February 1999
Java Tip 68: Learn how to implement the Command pattern in Java - JavaWorld - February 1999 |
Java Tip 71: Use
dynamic messaging in Java - JavaWorld - April
1999
Java Tip 71: Use
dynamic messaging in Java - JavaWorld - April
1999 |
Java Tip 42: Write
Java apps that work with
proxy-based firewalls - JavaWorld - December 1997
Java Tip 42: Write
Java apps that work with
proxy-based firewalls - JavaWorld - December 1997 |
Get smart with proxies and
RMI - JavaWorld
November 2000
Get smart with proxies and
RMI - JavaWorld
November 2000 |
Add XML to your J2EE applications - JavaWorld February 2001
Integrate an XML presentation layer in the J2EE layered architecture |
iContract: Design by Contract in Java - JavaWorld February
2001
iContract: Design by Contract in Java - JavaWorld February
2001 |
Clean up your wire protocol with SOAP, Part 4 - JavaWorld July
2001
Clean up your wire protocol with SOAP, Part 4 - JavaWorld July
2001 |
J2EE clustering,
Part 2 - JavaWorld August 2001
J2EE clustering,
Part 2 - JavaWorld August 2001 |
Explore the
Dynamic Proxy API
Explore the
Dynamic Proxy API |
J2SE 1.4
premieres Java's
assertion capabilities, Part 2
J2SE 1.4
premieres Java's
assertion capabilities, Part 2 |
Implement Design
by Contract for Java using dynamic proxies
Implement Design
by Contract for Java using dynamic proxies |
Take control with the Proxy design pattern
Take control with the Proxy design pattern |
Interface Tool for Java
Interface Tool for Java
Interface Tool for Java is a tool that allows Java programs to communicate with ActiveX objects. It allows easy integration of ActiveX objects into a Java Environment |
Impressive
!
Impressive
! |
Very
interesting
Very
interesting |
RMI, Dynamic Proxies, and the Evolution of Deployment
RMI, Dynamic Proxies, and the Evolution of Deployment
Dynamic Generation of Stub Classes
This release adds support for the dynamic generation of stub classes at runtime, obviating the need to use the Java Remote Method Invocation (Java RMI) stub compi |
Second-generation aspect-oriented
programming
Second-generation aspect-oriented
programming |
Java validation with dynamic proxies
Decouple validation processes from your business object implementations. |
JSP (JavaServer Pages) is a standard for combining Java and HTML to provide dynamic content in web pages.
With JSP, you embed Java code in HTML using special JSP tags similar to HTML tags. You install the JSP page, which has a .jsp extension, into the WebLogic Server document root, just as you would a static HTML page. When WebLogic Server serves a JSP page.. |
|
|
|