The Source for Java Technology Collaboration
User: Password:



   

Separating Concerns and Advising Domain Objects Separating Concerns and Advising Domain Objects

by Eric Batzdorff
01/30/2007

Being able to "separate concerns" is a fundamental tenet of object-oriented programming. Give an object a small, well-defined set of responsibilities; and then assemble many of those small objects to provide a larger set of responsibilities. Following this pattern of keeping concerns separate enables our code to be 1) modular in the case that it needs to be reused, 2) testable because the code is separated into isolated units, and 3) agile in the case that requirements change. It is the challenge of assembling those objects that we'll explore in this article.

Let's look at an example of a PersonService interface that has a save() method. Clients will use this interface to save a Person object. The core implementation of this method persists a given Person to the underlying database. The implementation is tested and it works.

/**
 * Person Service Interface
 */
public interface PersonService {
   public void save(Person person);
}

/**
 * Person Service Implementation
 */
public class PersonServiceImpl implements PersonService{
   public void save(Person person){
      doPersonSaveStuff(person);
   }
   private void doPersonSaveStuff(Person person){
      // implementation details of saving a person
   }
}

When clients call the PersonService.save() method, a security check is needed to enforce that the client is indeed allowed to save that Person. So a SecurityService is introduced with its isSecure() method. SecurityService.isSecure() is tested to ensure that it properly enforces security.

/**
 * Security Service Interface
 */
public interface SecurityService{
   public boolean isSecure();
}

/**
 * Security Service Implementation
 */
public class SecurityServiceImpl implements SecurityService{
   public boolean isSecure(){
      // returns true...this is just an illustration
      return true;
   }
}

There are now two distinct classes, PersonServiceImpl and SecurityServiceImpl, that both need to be called at runtime when a client calls the PersonService interface's save() method. The simplest way to do this is to, within the save() method of the PersonService implementation, make a call out to SecurityService.isSecure().

/**
 * Person Service Interface
 */
public class PersonServiceImpl implements PersonService{
   public void save(Person person){
      if (securityService.isSecure()){
         // if security check passes, do person save
         doPersonSaveStuff(person);
      }else{
         throw new IllegalAccessException("Could not save "
                + " person: " + person);
      }
   }
   private void doPersonSaveStuff(Person person){
      // implementation details of saving a person
   }

   private SecurityService securityService;
   public void setSecurityService(SecurityService securityService){
      this.securityService = securityService;
   }
}

This approach, however, is at odds with object-orientation and the goal of separating concerns. By combining the code in this way, the save() method is now less modular because it cannot be used in the absence of the security check. Additionally, the save() method is less testable because the Person-saving cannot be tested, in isolation, separate from the security-checking. The problem to be solved here is how to keep the person-saving concern separate from the security-checking concern, but combine them at runtime when appropriate.

The theory of Aspect-Oriented Programming (AOP) formalized the problem definition laid out in the example above, and there have since been many implementations to help developers assemble their objects at runtime. A good starting point to learn more about AOP and various implementations is Wikipedia's AOP article. In AOP terms for the example above, we needed to "advise" the PersonService's save() method with the security "advice" by "weaving" it in when a client calls save(). These AOP terms will be used going forward in this article.

There are many factors that go into choosing an approach for assembling concerns at runtime. Perhaps the biggest factor, though, is whether or not the object being advised is a singleton. In my opinion, there exist well-documented and well-implemented approaches for advising singletons. I personally enjoy Spring AOP when I need to advise singletons. Recently however, I came across the need to advise a domain object, an object that would be created frequently. I searched online for the best approach and I was pointed to AspectJ. This was unsatisfactory for me because AspectJ's learning curve and different compiler were too invasive for my need to advise just one domain object. In the remainder of this article, we will explore 1) why advising singletons is so different from advising domain objects, and 2) some alternatives to AspectJ for advising domain objects.

Advising Singletons Versus Domain Objects

A singleton, a class instantiated once, and a domain class, a class instantiated frequently, illustrate the opposing extremes of object-creation frequency. I have read and seen for myself that common techniques to advising singletons do not perform as well when applied to domain objects. The problem arises for domain objects due to the frequency with which they get created. The approach that Spring takes for advising singletons is to create a proxy for the singleton at runtime that will intercept its method calls. When clients make a call to the method, this proxy will configurably coordinate calls to the necessary advisors. Spring takes one of two approaches to create a proxy at runtime: either a Java dynamic proxy or a CGLIB code-generated proxy. Either way the proxy is created, the creation time is significantly slower than creating a regular object using Java's new keyword. For a singleton, one instance of the class will be created, so the time it takes to create the proxy for that object is not a large concern. For domain objects that would be created frequently, the performance of creating a runtime-generated proxy along with each object being advised may not be suitable. The following timings show the difference between creating a dynamic proxy as opposed to creating a regular object using the new keyword.

1000000 iteration test for 'Create dynamic proxy using Proxy.newProxyInstance()'
WARMED UP in 16 ms
Iterations ,Total Time(ms) ,Avg(ms)/Iteration ,Transactions/sec
1000000    ,7978            ,0.007978         ,125344.71

1000000 iteration test for 'Create regular object using new'
WARMED UP in 0 ms
Iterations ,Total Time(ms) ,Avg(ms)/Iteration ,Transactions/sec
1000000    ,63              ,0.000063         ,15873015 

Note that creating a CGLIB proxy is even slower than creating a Java dynamic proxy, but method invocations through the CGLIB proxy will perform better than the Java dynamic proxy.

Advising Domain Objects

What we need are some techniques to advise domain objects that will not significantly slow the object creation times of those advised objects and its advisors.

Pages: 1, 2, 3

Next Page » 

View all java.net Articles.

 Feed java.net RSS Feeds