Introduction: Contract design (designed by Contract) is a practical technique that clarifies the details of component design, records the correct component usage for the customer, and programmatically tests the compliance (compliance) used by the component. In the last installment of Aop@work , Dean Wampler introduced contract4j, a contractual design tool that assigns contracts with java™5 notation (annotation) and calculates contracts in AspectJ terms at run time. While becoming an important new tool in the AOP Toolkit, CONTRACT4J caters to new trends in aspect-oriented design.
Let's say you just joined a project to build a banking application. As you navigate through the code, you find the following interface to BankAccount (Simplified):
interface BankAccount {
float getBalance();
float deposit(float amount);
float withdraw(float amount);
...
}
The above interface, though concise, has left many unanswered questions. Can the amount parameters of deposit () or withdraw () be negative or 0? Do you allow negative balances (overdraft)? What happens if the wrong amount,deposit () or withdrawal () is specified?
Obviously, it is important to be able to answer these questions for the implementation of the interface and for those using the components that expose the interface. An implicit way to specify behavior is to use unit tests written in JUnit (see Resources). Using JUnit tests, you can invoke these methods with various legitimate and illegal parameters and make assertions about the expected result behavior. Another approach is contract design, which is a practical technique for clarifying component design details.
In the final installment of the Aop@work series, I'll introduce contract4j, a AspectJ based tool that supports contract design. I'll explain how to implicitly specify component behavior with CONTRACT4J, record the correct usage of components for users, and programmatically test the compliance of component usage. At the end of this article, I'll discuss how contract4j meets the emerging trends in aspect-oriented design.
Overview of Contract design
With a contract design, you can specify requirements for component input and return results in a programmable expression. The expression is evaluated during the developer and QA tests, and if the test fails, the program execution terminates immediately. Termination of the program with useful diagnostic information forces the developer to fix the bug immediately.
Forcing immediate termination may seem a bit cumbersome. Why let go of the error message and continue to run? Although it may seem more productive to continue running, it is not actually. First, if you are not forced to process bugs immediately, you will postpone fixing bugs so that the bugs accumulate. Second, failed tests should represent unexpected events (for example, the reference is empty) and normal execution cannot continue. Although you can put the "unexpected processing" code in place, this may complicate the implementation and will never happen, increasing the complexity of the code and the risk of more bugs.