Skip to main content

Separating Concerns and Advising Domain Objects

January 30, 2007

{cs.r.title}



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 href="http://en.wikipedia.org/wiki/Singleton_pattern" name="Singleton">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 name="AspectJ">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
name="CGLIB">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.







Object Pool

One option is to pool the created objects. The benefit of this
approach is that we can still have the flexibility of a
runtime-generated proxy for advising domain objects while
eliminating much of the object-creation performance concerns. A
side note is that Spring makes it very easy to pool objects and
retrieve objects from that pool. The drawbacks of this approach are
that we now have to deal with:

  • Returning the advised objects back to the pool when clients are done using them.
  • Clearing the state within an object before it is used again by another client.
  • Hoping that clients do not keep references to any objects that were released back to the pool. If clients do not cooperate, this could cause all sorts of hard-to-track-down data integrity issues.
  • Tuning the maximum size of the pool appropriately. The size would need to be based on the hardware, amount of memory allocated to the JVM, etc.

So pooling is an option, but I'm not particularly fond of the
external and internal complexity it adds to what should be simple
objects. By having these additional considerations, the objects are
all of a sudden not so simple.

The Setup

For the remainder of the discussion, we'll look at a simple
Person domain object with the following interface and
corresponding implementation:

/**
* Interface of a Person domain object
*/
public interface Person {

   public String getName();
   public void setName(String name);

   public long getId();
   public void setId(long id);

   public Collection getAddresses();
   public void setAddresses(Collection addresses);

   public boolean isModified();
   void markModified();
}

/**
* Implementation of the Person domain object
*/
public class PersonImpl implements Person {
   private long id;
   private String name;
   private Collection addresses;
   private boolean modified;

   public Collection getAddresses() { return addresses; }
   public void setAddresses(Collection addresses) {
      this.addresses = addresses;
   }

   public long getId() { return id; }
   public void setId(long id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public boolean isModified(){ return modified; }
   public void markModified(){ modified = true; }
}
Class Extension

A simple approach to advising Person would be to
extend the core implementation using Java class extension. For
example, here is an advisor for the getAddresses()
method that will lazily load the addresses for a
Person:

public class AddressLazyLoadingProxy
                            extends PersonImpl{
   /**
    * Intercepts calls to getAddresses() and loads them
    * if they have not already been loaded.
    */
   public Collection getAddresses(){
      Collection addresses = super.getAddresses();
      if (addresses == null){
         addresses = lazyLoadAddresses();
         super.setAddresses(addresses);
      }
      return addresses;
   }

   private Collection lazyLoadAddresses(){
      /*
       * in reality, this would delegate to some DAO
       */
      Collection addresses = new ArrayList();
      Object address1 = new Object();
      addresses.add(address1);

      Object address2 = new Object();
      addresses.add(address2);
      return addresses;
   }
}

The class extension approach is straightforward and easy to
implement for this single advisor. However, if additional advisors
are added, this approach quickly becomes unwieldy as each advisor
extends another advisor. There is hardly any flexibility. Without
changing class structure, we cannot pick and choose which advisors
are used with Person and cannot change the order of
the advisors. Even if there is no runtime requirement to change the
advice structure, the advice structure is not agile if requirements
do change.

Static Decorating Proxy

Another option we'll explore is using the Gang of Four's Decorator pattern to decorate the
domain object with any number of advisors in any order. To set this
up, we would first create a base proxy class from which all our advice
proxies would extend. This base proxy will accept any
implementation of Person in its constructor and will
delegate all method calls to that target person. Here's a portion
of that class:

/**
* Base class for all Person proxies following
* the Decorator pattern.
*/
public class BasePersonProxy implements Person {
   private Person targetPerson;

   /**
    * Construct the proxy and provide the class that will be
    * called to proceed down chain of proxies.
    *
    * If the passed in object is not a proxy, then this will
    * delegate directly to the Person object.
    *
    * @param targetPerson
    */
   public BasePersonProxy(Person targetPerson){
      this.targetPerson = targetPerson;
   }

   public long getId() { return targetPerson.getId(); }
   public void setId(long id) { targetPerson.setId(id); }

   public Collection getAddresses() {
      return targetPerson.getAddresses();
   }
   public void setAddresses(Collection addresses) {
      targetPerson.setAddresses(addresses);
   }

Using this pattern, our lazy-loading advice looks almost
identical to how it looked in the Class Extension example; the only
differences being that it now extends from
BasePersonProxy instead of from
PersonImpl and it has a constructor that takes the
Person being proxied.

/**
* Proxy that will lazily load a Person's addresses
*/
public class AddressLazyLoadingProxy extends BasePersonProxy{
   public AddressLazyLoadingProxy(Person targetPerson) {
      super(targetPerson);
   }

   public Collection getAddresses(){
      Collection addresses = super.getAddresses();
      if (addresses == null){
         addresses = lazyLoadAddresses();
         super.setAddresses(addresses);
      }
      return addresses;
   }

   private Collection lazyLoadAddresses(){
      /*
       * in reality, this would delegate to some DAO
       */
      Collection addresses = new ArrayList();
      Object address1 = new Object();
      addresses.add(address1);

      Object address2 = new Object();
      addresses.add(address2);
      return addresses;
   }
}

Note that because BasePersonProxy provided default
implementations for every Person method, the
AddressLazyLoadingProxy only needed to implement the
methods it chose to proxy.

To create a Person that is proxied with the
lazy-loading advice, the code could look something like the
following:

/**
* Creates Person
*/
public Person createPerson() {
   // create the target person
   Person person = new PersonImpl();

   // pass the target person being proxied in to the new proxy
   person = new AddressLazyLoadingProxy(person);

   // return the proxied person
   return person;
}

If we have other advisors following this static decorator
pattern, they would each extend from BasePersonProxy
and they would each be created in a similar manner. For
example:

/**
* Creates Person
*/
public Person createPerson() {
   Person person = new PersonImpl();
   person = new AddressLazyLoadingProxy(person);
   person = new PersonProxy_2(person);
   person = new PersonProxy_3(person);
   person = new PersonProxy_n(person);
   return person;
}

Taking this approach, we can have any number of advisors for
Person. We can easily pick and choose which ones we
use and can easily choose the order in which they advise
Person. Also, as the Decorator pattern suggests, the
class hierarchy stays shallow, which usually enables flexibility. To
see an exhaustive discussion of the Decorator pattern, I refer you
to the Gang of Four's book on design patterns. This advice pattern
is described as "static" because the method(s) being advised,
getAddresses() in this case, is statically baked into
the class structure of the proxy. This is different from a dynamic
proxy approach where reflection can be used to
evaluate whether or not to advise the method. From an AOP
perspective, this static decorating pattern becomes less attractive
when a specific advisor needs to advise many methods on the class.
Let's look at a person-modification advisor that will advise every
setXXX() method on the Person
implementation by calling the Person's
markModified() method after making the
setXXX() call on the target. For example:

/**
* Proxy that will mark the target person as modified
*/
public class PersonModificationProxy extends BasePersonProxy {

   /**
    * Constructs an person modification proxy with
    * targetPerson as the proxy target
    *
    * @param targetPerson target of this proxy
    */
   public PersonModificationProxy(Person targetPerson) {
      super(targetPerson);
   }

   public void setName(String name){
      super.setName(name);
      super.markModified();
   }

   public void setId(long id){
      super.setId(id);
      super.markModified();
   }

   public void setAddresses(Collection addresses){
      super.setAddresses(addresses);
      super.markModified();
   }
}

For the AddressLazyLoadingProxy class, that proxy
was all about the getAddresses() method. This
person-modification proxy is markedly different in that it is about
many methods; every setXXX() method to be exact. We
needed to extend every setXXX() method and each method
implementation has similar code. Not only that, but for setters added
over time, we would need to keep this advisor up to date so that
those setters would also be proxied. The duplicate code in each
method and the necessary upkeep of this class both indicate that
there must be a better way. Ideally, we would be able to call
markModified() for any
setXXX() call without the need to explicitly proxy
each setXXX() method. We really need the use of a
dynamic proxy so that every method call on Person can
go through an InvocationHandler, like the
following:

public class PersonModificationIh implements InvocationHandler{
   public Object invoke(Proxy proxy, Method method, Object[] args)
                 throws Throwable {
      // call the method the client wanted to call
      Object returnObject = method.invoke(targetPerson, args);

      // call markModified() for setXXX() method calls
      if (method.getName().startsWith("set")){
         targetPerson.markModified();
      }


      return returnObject;
   }
}

What's missing from this code snippet is where
targetPerson came from. If a dynamic proxy cannot
exist per-instance due to performance concerns, then the
targetPerson cannot exist as a member of the
PersonModificationIh class. This next pattern will
look at a mechanism to make targetPerson available to
the invocation handle while still supporting the flexibility that
the Decorator pattern provided for advising a
Person.







Dynamic Decorating Proxy

As we saw earlier, we cannot create a dynamic proxy for each
Person instance if object creation times are critical.
Instead, in this pattern we will create a singleton dynamic proxy
for an interface that looks similar to a Person, but
where each method takes the target Person as the first
parameter. This interface is essentially a stateless version of the
Person interface:

/**
* Stateless equivalent of the Person interface.
*/
public interface StatelessPerson {
   long getId(Person targetPerson);
   void setId(Person targetPerson, long id);

   String getName(Person targetPerson);
   void setName(Person targetPerson, String name);

   Collection getAddresses(Person targetPerson);
   void setAddresses(Person targetPerson, Collection addresses);

   boolean isModified(Person targetPerson);
   void markModified(Person targetPerson);
}

Because every method accepts the target person to be operated
on, an implementation of this interface can be stateless and a
singleton. Given this, here is the singleton dynamic proxy for the
StatelessPerson that does the person-modification
logic we want using reflection:

/**
* Object Modification Advice for a StatelessPerson
*/
public class PersonModificationIh implements InvocationHandler{

   public Object invoke(Proxy proxy, Method method, Object[] args)
                                            throws Throwable {
      // call the method the client wanted to call
      Object returnObject = this.proceed(method, args);

      // call markModified() for setXXX() method calls
      if (method.getName().startsWith("set")){
         Person targetPerson = (Person)args[0];
         targetPerson.markModified();
      }

      return returnObject;
   }

   private static final Class statefulClass = Person.class;
   /**
    * Calls the stateful equivalent of the stateless method.
    * This could be factored out to a base class or a reusable
    * utility.
    */
   private Object proceed(Method statelessMethod,
                      Object[] statelessArgs) throws Throwable {
      // statefulObject is assumed to be the first arg
      Object statefulObject = args[0];

      // locate stateful method based on stateless one
      Method statefulMethod=findStatefulMethod(statelessMethod);

      // convert the stateless args to stateful args
      Object[] statefulArgs=new Object[statelessArgs.length-1];
      System.arraycopy(args, 1, statefulArgs, 0,
                                           statefulArgs.length);

      try{
         // invoke the stateful method on the stateful object
         return statefulMethod.invoke(statefulObject,
                                                 statefulArgs);
      }catch(InvocationTargetException ite){
         throw ite.getCause();
      }
   }

   /**
    * Given a stateless method on a stateless class, returns
    * the stateful equivalent on the corresponding stateful
    * class.  This could be factored out to a base class
    * or a reusable utility.
    */
   private Method findStatefulMethod(Method statelessMethod){
      // this could easily be cache'd to be more efficient
      Class[] statelessParmTypes = statelessMethod.
                                           getParameterTypes();
      Class[] statefulParmTypes =
                       new Class[statelessParmTypes.length - 1];
      System.arraycopy(statelessParmTypes, 1, statefulParmTypes,
                                   0, statefulParmTypes.length);
      Method statefulMethod = null;
      try {
         statefulMethod = statefulClass.getMethod(
                  statelessMethod.getName(), statefulParmTypes);
      } catch (Exception e) {
         throw new RuntimeException("Could not find stateful " +
         " equivalent of stateless method: " + statelessMethod);
      }
      return statefulMethod;
   }
}

Given this, we can create a Person decorator for
our original stateful Person that leverages the
stateless Person. Here's a portion of that class:

public class PersonDynamicProxy implements Person{
   private StatelessPerson statelessPerson;
   private Person targetPerson;


   /**
    * Construct a Person dynamic proxy where all method calls
    * are delegated to the statelessPerson.
    *
    * @param targetPerson
    * @param statelessPerson
    */
   public PersonDynamicProxy(Person targetPerson,
                    StatelessPerson statelessPerson){
      this.targetPerson = targetPerson;
      this.statelessPerson = statelessPerson;
   }

   public long getId() {
      return statelessPerson.getId(targetPerson);
   }

   public void setId(long id) {
      statelessPerson.setId(targetPerson, id);

   }

   public String setName(String name) {
      statelessPerson.setName(targetPerson, name);
   }

Figure 1 below shows the high-level runtime
interaction we achieve by this design:

Dynamic Proxy Interaction
Figure 1. Dynamic proxy interaction

Before the interactions in Figure 1 occurred, the
client obtained a Person implementation. This
implementation was decorated by a PersonDynamicProxy
object which held a reference to 1) a PersonImpl
instance, targetPerson, and 2) a runtime generated
PersonModificationDynamicProxy dynamic proxy whose
invocation handler was PersonModificationIh. Let's now
look at the interactions shown in Figure 1:

  1. The client calls the setName(String) method on its
    Person implementation. The setName(String) method on
    the PersonDynamicProxy is invoked.

  2. PersonDynamicProxy, in turn, calls the
    setName(Person, String) method on the
    PersonModificationDynamicProxy, passing in the target
    Person it should operate on. As a reminder, here's the
    PersonDynamicProxy code that made the call to the
    runtime generated dynamic proxy:

    public String setName(String name) {
       statelessPerson.setName(targetPerson, name);
    }
  3. The invoke() method is then invoked on the
    PersonModificationIh, the invocation handler for the
    runtime generated PersonModificationDynamicProxy.
    Within the invoke() method, the equivalent method,
    setName(String), is invoked on the
    targetPerson using reflection.

  4. PersonModificationIh then determines that the name
    of the invoked method started with "set," so it also called the
    markModified() method on targetPerson
    using reflection.

With this approach, we now have an advisor that can advise every
setXXX() method and does not have to change if more
setXXX() methods are added to the Person
interface. Because we took a decorating approach for both the
static and dynamic proxy, we can mix and match our static proxies
with our dynamic proxies in any order we want. Additionally, all of
the frequent object creation is done with the Java new
keyword, so object creation times are not sacrificed. Here is some
example code that creates a person with both the static
address lazy-loading advice and the dynamic person-modification
advice:

/**
* Creates a Person with two advisors: the static lazy-loading advisor
* and the dynamic person-modification advisor.
*/
public Person createPerson() {
   Person person = new PersonImpl();
   person = new PersonDynamicProxy(person,
                       statelessModificationAdvice);
   person = new AddressLazyLoadingProxy(person);
   return person;
}

/**
* Stateless Object Modification advice
*/
private static StatelessPerson statelessModificationAdvice=null;
static{
   // initialize the stateless person-modification advice singleton
   ClassLoader cl = PersonFactoryImpl.class.getClassLoader();
   InvocationHandler ih = new PersonModificationIh();
   Class[] interfacesToProxy = new Class[]{StatelessPerson.class};
   statelessModificationAdvice = (StatelessPerson)Proxy.
     newProxyInstance(cl, interfacesToProxy, ih);
}

The drawbacks to this stateless dynamic proxy approach are the
following:

Conclusion

Advising objects to separate concerns is an extremely powerful
pattern. Spring-like approaches to AOP are fantastic choices if few
instances of the advised class need to be created. However, many
domain objects do not fall in this category. AspectJ is a
full-featured AOP implementation that is designed to advise both
singletons and domain objects. However, AspectJ is not a trivial
technology to bring into a development environment, so knowing
alternatives is a good thing. For advising domain objects, we
explored object pooling, class extension, and using the Decorator
pattern, and there are undoubtedly others. Depending on the
scenario, each approach enables a separation of concerns that
object-oriented programming and aspect-oriented programming
advocate. What I attempted to illustrate in this article were some
factors that go into choosing an advice approach and some options
available for advising domain objects.

Resources


width="1" height="1" border="0" alt=" " />
Eric Batzdorff currently works at TRIRIGA as a J2EE developer, designer, and architect.
Related Topics >> Programming   |