Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Monitoring and Enforcing Design Dependencies with Verifydesign : Page 2

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

Finding Unwanted Dependencies
First, I'll show you what happens if a developer makes one of the most basic mistakes in a system—circumventing a well-defined API. Open up the file verifydesign/packagedesign/input/javasrc/biz/xsoftware/impl/client/Client.java, and add the following code to the method called createPhoneTheWrongWay():

PhoneImpl phone = new PhoneImpl();

Figure 4. Unwanted Dependency Change: According to the design, the client should depend only on the phoneApi. Verifydesign catches this mistake
Figure 4 shows a graphical representation of what was changed in the source code; the red line illustrates the inserted dependency. Note that this represents the implemented design, not the desired "package design" defined in the design file.

After making the change, run the build again, using the same command:

ant --f bldfiles/build.xml

Instead of the success message from your previous build, you'll now see the following error:

You are violating your own design.... Class = biz.xsoftware.impl.client.Client depends on Class = biz.xsoftware.impl.phone.PhoneImpl The dependency to allow this is not defined in your design Package=biz.xsoftware.impl.client is not defined to depend on Package=biz.xsoftware.impl.phone Change the code or the design

Notice the last line of the message, "Change the code or the design." That reflects the reality that the package design always evolves in every software project that exists. This statement is true whether you use Verifydesign or not. Verifydesign has been used on projects with both Agile and Waterfall processes, which have shown that no matter the process, designs always change. Verifydesign simply blatantly points out how designs evolve rather than having such design changes go unnoticed.

Before you continue, remove the breaking change to restore the file to its original state. You should once again be able to build the project successfully.

External Dependencies
Sometimes, you want to isolate an external dependency so that only one package in your source code depends on that external entity. This is very useful if you think you might need to switch the external technology later. By limiting the external dependency to a single package, you ensure that switching entails only rewriting the one dependent package. By default, Verifydesign makes you declare dependencies on anything in javax. Also, by default, it does not require you to declare dependencies on java.*. You can override both these defaults.

Assume that the Phone class depends on java.rmi and all subpackages of java.rmi, including java.rmi.registry. Also assume that you know you will have to change that later, and you don't want other packages (such as the client) to depend directly on javax.rmi. For example, perhaps you might later want to use a different phone implementation that depends on Web services in addition to the phone implementation that depends on rmi. Or suppose you know in advance that you might eventually want two different Phone implementations.

To enforce this, the first thing you need to do is override the java.* default behavior—but just for java.rmi. You'd make this change to the design.xml file:

<package name="rmi" package="java.rmi" subpackages="include"/> <package name="phoneImpl" package="biz.xsoftware.impl.phone"> <depends>phoneApi</depends> <depends>rmi</depends> </package>

The preceding configuration adds a subpackages attribute, telling the design tool that you want to include all subpackages of java.rmi in the rmi package. This means that the PhoneImpl package can now depend on these packages:

  • java.rmi
  • java.rmi.activation
  • java.rmi.dgc
  • java.rmi.registry
  • java.rmi.server

Now, go ahead and add the following code to PhoneImpl.java:

Figure 5. Good Encapsulation: Note how the client and phoneApi no nothing about the rmi module. Only the PhoneImpl component (the implementation of the phoneApi) knows about rmi.

public void makeCall(String number) { try { Registry registry = LocateRegistry.createRegistry(5000); } catch(RemoteException e) { throw new RuntimeException(e); } }

A graphical representation of the current "package design" would now look like Figure 5.

This is encapsulation at its best. The client and API will not be allowed to know about RMIExceptions. Note that you cannot throw a RemoteException on the makeCall method, because if you did, you'd have to add the throws RemoteException clause to Phone.java's makeCall method in the API package—and that would violate the design by introducing a dependency from the phoneApi to java.rmi.RemoteException. That dependency would then cause the build to fail, because the phoneApi package is not allowed to depend on java.rmi.RemoteException. Remember, only the phone implementation is allowed to depend on java.rmi. The API should not depend on rmi, because you want the client to use an API that is generic and not tied to any specific technology. I leave this for you to try yourself.

Comment and Contribute






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