Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Monitoring and Enforcing Design Dependencies with Verifydesign : Page 4

Verifydesign lets you codify a design in XML and run compile-time checks to notify developers of dependencies that break the design specifications.


advertisement
Using Verifydesign on Legacy Systems
In many cases, you may have already inherited someone else's software or are joining a team with an existing product. In most cases, you inherit something like Figure 1. Obviously you would like to clean up the dependencies. You can do that with Verifydesign as well.

In the downloadable code, you can find the code that represents the system in Figure 1 in the verifydesign/legacy/input/javasrc folder.

You'd start the process by defining a design file that allows the build to pass. Don't worry about APIs at this point. Here's the first design file you might write (see the file verifydesign/legacy/bldfiles/design.xml in the downloadable code):

<design> <package name="componentA" package="biz.xsoftware.componentA" needdeclarations="false" needdepends="false"/> <package name="componentB" package="biz.xsoftware.componentB" needdeclarations="false" needdepends="false"/> <package name="componentC" package="biz.xsoftware.componentC" needdeclarations="false" needdepends="false"/> <package name="componentD" package="biz.xsoftware.componentD" needdeclarations="false" needdepends="false"/> <package name="componentE" package="biz.xsoftware.componentE" needdeclarations="false" needdepends="false"/> </design>

 
Figure 7. Cleaning Up: The figure illustrates the dependency that needs to be cleaned up.
The needdepends="false" attribute in the preceding code provides a way to specify that your package does not need to declare any dependencies. Similarly the needdeclarations="false" attribute specifies that dependent packages don't have to declare their dependencies either. These attributes are particularly useful for javax.swing so you can enter needdeclarations="false" once, and avoid writing swing on every single component that depends on swing. Now, go to the verifydesign/legacy directory and run the command below to make sure that the build succeeds:

ant --f bldfiles/build.xml

On to the next step. In a legacy system, the business goal is to keep delivering business value while "slowly" cleaning up the design. For example, in release one, you might target one dependency you don't like and clean that up. In Figure 7, the red arrow highlights the dependency to be removed first

To do that, you need to find all the classes that cause Component E to depend on Component C. I've removed the needdepends attribute from componentE in the preceding code.



<design> <package name="componentA" package="biz.xsoftware.componentA" needdeclarations="false" needdepends="false"/> <package name="componentB" package="biz.xsoftware.componentB" needdeclarations="false" needdepends="false"/> <package name="componentC" package="biz.xsoftware.componentC" needdeclarations="false" needdepends="false"/> <package name="componentD" package="biz.xsoftware.componentD" needdeclarations="false" needdepends="false"/> <package name="componentE" package="biz.xsoftware.componentE" needdeclarations="false"/> </design>

At this point, you'll need to declare everything componentE depends on or the build will break. In this case, because all the other packages have needdeclarations set to false, the build will break only if componentE depends on external packages. You can now run the build command ant—f bldfiles/build.xml on the legacy system and see what happens.

You'll find that Component E depends on something in Swing. That's fine, just add a new package for Swing and allow everyone to depend on it (which may or may not be a good idea depending on your system). Add this line to the top of the design.xml file.

<package name="swing" package="javax.swing" subpackages="include" needdeclarations="false"/>

With this new line, the build should now succeed again. You can run the build and verify. Now the external dependencies that the componentE package depends on are resolved, and you can move on to the next package, using the same techniques to discover anything it depends on in the componentC package. Remove the needdeclarations attribute from componentC, forcing components without the needdepends attribute to define any dependencies on componentC. Here's the new design file with changes in bold.

<design> <package name="swing" package="javax.swing" subpackages="include" needdeclarations="false"/> <package name="componentA" package="biz.xsoftware.componentA" needdeclarations="false" needdepends="false"/> <package name="componentB" package="biz.xsoftware.componentB" needdeclarations="false" needdepends="false"/> <package name="componentC" package="biz.xsoftware.componentC" needdepends="false"/> <package name="componentD" package="biz.xsoftware.componentD" needdeclarations="false" needdepends="false"/> <package name="componentE" package="biz.xsoftware.componentE" needdeclarations="false"/> </design>

Now anything that depends on componentC must declare that they do, except for components with needdepends="false"—which for right now is all of them except Component E. Run the build again and the error will be

You are violating your own design.... Class = biz.xsoftware.componentE.SubComponentE depends on Class = biz.xsoftware.componentC.ComponentC The dependency to allow this is not defined in your design Package=biz.xsoftware.componentE is not defined to depend on Package=biz.xsoftware.componentC Change the code or the design

Based on that error message, you can go to the SubComponentE class, which depends on the ComponentC class and fix the dependency in the code. At this point, your code now adheres to the new design. This demonstration legacy project contained only one dependency tying ComponentE to ComponentC, but in a real system, you'll probably need to clean up many, many dependencies. Verifydesign will tell you all of them.

Fixing the "componentE depends on componentC" problem is left up to you. I will leave you with a hint though. Think about using a listener in componentE and having componentC's classes implement that listener. A discussion of using design patterns to reverse dependencies would require a complete article by itself, so I will not go into more detail on that subject here.

One Caveat
There is one small caveat with Verifydesign I have not mentioned. Verifydesign cannot check dependencies on constants. For example, suppose you have two classes:

biz.xsoftware.impl.client.Client biz.xsoftware.impl.phone.PhoneImpl

Furthermore, suppose Client.java depends on a field in PhoneImpl.java defined as follows:

public static final String someConstant = "xyz";

When you compile Client.java, the bytecode will contain "xyz" and will give no indication that Client depended on phoneImpl. And because Verifydesign analyzes bytecode to check out all the dependencies, it cannot catch this mistake. With all other dependencies phoneImpl would have been referenced by Client.class in the bytecode such that Verifydesign could flag the violation of the design.. Unfortunately, only Sun can fix this problem—by adding to the Java Language Specification. This problem does occur once in a while, but even without Verifydesign's help, these are the easiest of all dependencies to correct. Overall, VerifyDesign does a great job of guaranteeing that your code follows the "package design" you want.

You've now seen an overview of Enterprise Architecture Preservation (EAP) in action assisted by the Verifydesign tool. You have seen how a team might use it, and how to deploy it on a legacy system. It's often surprising to see all the dependencies that have crept in when you deploy this tool on a legacy system. Typically, developers find Verifydesign annoying to use at first, but after a few weeks of use, you start to really see the value. It catches unwanted dependencies at every step. These unwanted package dependencies are your package design so Verifydesign forces you to think about how code changes might change that design. Maybe someone, someday, will create an Ant task to generate a slick graphical picture of the design from the design file, or integrate visual design into our favorite IDEs.

Naturally Verifydesign sets up an architecture so its much easier to start putting unit tests around the components themselves. A library called mocklib was written specifically to be compatible with Verifydesign. Mocklib has the ability to simulate entire APIs, meaning it can simulate componentE's API to test componentC. Then, you can completely refactor componentC without rewriting a single test in the test suite. Test Suites change only when the component APIs change. Feel free to check that project out as well.

To close, here are a few links to projects that currently use Verifydesign. You can download these open source projects and look at their "package designs." Note that the Java Sip project has two layers; you can grab the bottom layer and be guaranteed you won't have to take all the other code, which is quite nice. Interestingly, all the projects below also use mocklib. The javasip project uses mocklib to simulate the entire channelmanager API (not just a single interface). Also check out the Java State Machine project, and the NIO Abstraction project.



Dean Hiller creator of Verifydesign and Mocklib, is the Director of Architecture, where he uses tools like verifydesign to monitor evolving architectures, teaches teams how to be disciplined enough to follow true Agile processes. His teams produce products where the minimum amount of code tested by automated tests is 75 percent.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap