|
YOUR FEEDBACK
Did you read today's front page stories & breaking news?
SYS-CON.TV |
TOP THREE LINKS YOU MUST CLICK ON Enterprise AOP, IoC, and OO Design Patterns Without Frameworks
Finding the right balance
By: Jinsong Yang
Oct. 24, 2006 02:00 PM
I'd like to share some of the design highlights of a large-scale content distributing system I worked on a while back. Some of the highlights may seem trivial; some may be a little more complicated. To me, software design is a matter of finding a balance between applying available technologies and fulfilling real-world requirements and constraints. The goal of design is always to ensure both the runtime and development-time quality of the software.
Extensibility by OOP Patterns and Principles We've designed the Schedule-Manager class as the façade of this component. It exposes external APIs. Other components that need to interact with SMC do so through this class. ScheduleManager is also in charge of managing the lifecycles of classes inside SMC. One of the business requirements is that the first production release of the software won't support clustered deployment, but it will in later releases. One of the keys to supporting clustering is managing the states of the component instances deployed across the clustered nodes so that they're always in sync. Keeping this in mind, we decide to centralize the cluster-sensitive states of all the SMC classes into one class rather than having each class manage its own states. This results in a SchedulingContext class. This class knows how to save and retrieve various states of the component. When the time to support the cluster comes along, instead of having to open up each class and make changes, all we have to do is change the SchedulingContext class - that modifyies the way this class accesses states so that all the clustered instances share the same states, virtually or physically. The UML diagram in Figure 2 shows some major classes after introducing the SchedulingContext class. One issue we immediately notice with this design is the tight coupling between the SchedulingContext class and the classes that depend on it. As you can see, quite a number of classes depend on the SchedulingContext class. If we have to change the SchedulingContext class for any reasons (fixing bugs, adding new business features, switching to other application server, etc.), chances are we also have to make changes to the dependent classes. Following OO best practices, interfaces are good at promoting loose coupling. In fact, it's always a good idea to code to interfaces rather than concrete classes (another simple yet powerful OO principle). The solution is simple enough - we abstract the scheduling context by making SchedulingContext an interface. We also want this interface's client to be unaware of the actual implementation, and the GoF Abstract Factory Pattern does the job. The design of the Scheduling Context is shown in Figure 3. When it comes time to support clustering, we can do it with little programming. It makes it a lot easier to maintain the software after it goes into production. We also avoid vendor lock-in by shielding the cluster-sensitive implementation, which is likely to be vendor-specific. Another consequence of moving the states of an individual class into SchedulingContext is that we can now design other stateless classes, which is good because:
Decoupling with IoC Pattern With the current design, unit testing isn't a trivial task. Let's take the FNDTaskPuller class as an example. Listing 1 shows the simplified version of this class. For demonstration purposes, let's assume this class has a business method, someBizMethod(). To test this class, we'd create a test class similar to the one shown in Listing 2. What's the problem with this code? We're testing SchedulingContextSimpleImpl (which is the concrete class of SchedulingContext) indirectly. But we really mean to test the FNDTaskPuller class. This isn't right. As we all know a unit test should never go outside of its own class boundary. Furthermore, it's hard to control the states of the SchedulingContextSimpleImpl in order to test the different behaviors of FNDTaskPuller class. In practice, a common technique to overcome this kind of tight coupling is using mock objects, which can assist in separating unit tests. Mock objects themselves, however, require extra coding efforts. This extra coding effort can be significant, buggy, and cause maintenance problems. What more? Developers have to replace mock objects with real classes at deployment. The reason for this problem is the way we acquire the reference to the SchedulingContext object in the FNDTaskPuller class. In this case, the FNDTaskPuller class is asking for a reference to a SchedulingContext object. Explicitly. To get around this, we need to change the way we obtain an object reference. This is where Inversion of Control (IoC) comes into play. With IoC, objects obtain references to their dependent objects passively. The IoC container literally "injects" the dependency into the classes. Now, we need an IoC container for our design to wire the objects. We have the choice of using an existing container product or building our own. Normally in-house framework building is considered a bad practice because of its complexity and inefficiency. However, we decided to do it anyway after carefully figuring out what we really needed. Some of the reasons are:
All we have to do in the ScheduleManager class is to instantiate a SchedulingContext object and assign it to classes that need a reference to it. If, some time in the future, a full IoC container is needed, we can just modify the ScheduleManager class.
Taking Care of Scattered Code List 2 is an example of a how we would have implemented our data access methods without AOP. In Listing 4, only one line of the code is actually "doing something." The rest is just "plumping" code. When "plumping" code starts leaking into your application, chances are you'll find yourself hunting down all the application code when requirements change. There's one thing we can do. While OOP makes software design modular, AOP makes code modular. And modularity is good. Once again, we faced the choice of using an existing AOP framework or building our own. We decided not to use any of these frameworks for the same reason why we didn't use a IoC container. Instead, we separate the concerns programmatically in our code. Although this is not the most elegant, cleanest way, it's the fastest, which, in our case, is a big gain. To separate the cross-cutting concerns from the DAOs, we came up with the following class design:
LATEST JAVA STORIES & POSTS
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK SPONSORED BY INFRAGISTICS
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||