Abstract Factory

Using references to interfaces instead of references to concrete classes is an important way of minimizing ripple effects. The user of an interface reference is always protected from changes to the underlying implementation.

The Abstract Factory pattern is one example of this technique. Users of an Abstract Factory can create families of related objects without any knowledge of their concrete classes. (A typical business application would usually not need to use this technique, at least as applied to Data Access Objects.)

Example

An Abstract Factory is a major part of the full Data Access Object (DAO) scheme. Here, the idea is to allow the business layer to interact with the data layer almost entirely through interface references. The business layer remains ignorant of the concrete classes which implement the datastore.

There are two distinct families of items here:

Let's take the example of storing Device objects. They may be stored in either a text file or a relational database. Since the calling code needs to remain ignorant of which is being used, it's natural, of course, to define an interface that reflects this, along with two corresponding concrete implementations:

package myapp;

public interface DeviceDAO {

  Device fetch(String aId) throws DataAccessException;
  
  void add(Device aDevice) throws DataAccessException;
  
  void change(Device aDevice) throws DataAccessException;
  
  void delete(Device aDevice) throws DataAccessException;
} 

...with an implementation for a file system:

package myapp;

final class DeviceDAOFileSys implements DeviceDAO {
  
  /*
   * The constructor will usually be passed any required config data.
   */
  
  @Override public void add(Device aDevice) throws DataAccessException {
    //elided...
  }
  
  @Override public void change(Device aDevice) throws DataAccessException {
    //elided...
  }
  
  @Override public void delete(Device aDevice) throws DataAccessException {
    //elided...
  }
  
  @Override public Device fetch(String aId) throws DataAccessException {
    //elided...
    return null;
  }

} 

...and an implementation for a relational database:

package myapp;

final class DeviceDAORelational implements DeviceDAO {
  
  /*
   * The constructor will usually be passed any required config data.
   */
  
  @Override public void add(Device aDevice) throws DataAccessException {
    //elided...
  }
  
  @Override public void change(Device aDevice) throws DataAccessException {
    //elided...
  }
  
  @Override public void delete(Device aDevice) throws DataAccessException {
    //elided...
  }
  
  @Override public Device fetch(String aId) throws DataAccessException {
    //elided...
    return null;
  }
  
} 

(The details of these classes are left out, so that you can see the structure better.)

Next, the calling application needs a way to interact with the database, without knowing about the concrete implementations. This is done using a factory class:


package myapp;

/**
 Returns all DAO instances.
  
 Reads a configuration item (defined by your program) to decide 
 which family of DAO objects it should be returning, for the currently 
 running program, either file-based or relational-based.
  
 The configuration mechanism may be a System property, a properties file, an
 XML file, and so on. The config is often read when the system initializes,
 perhaps using a static initializer.
*/
final class DAOFactory {
  
  /* some would implement all of the methods here as static methods */
  
  DeviceDAO getDeviceDAO(){
    //elided:
    return null;
  }

  UserDAO getUserDAO(){
    //elided:
    return null;
  }
  
} 



package myapp;

public final class Application {
  
  /**
   This calling code is completely unaware of the underlying datastore.
   It could be a text file, or a relational database.
  */
  void addNewDevice(Device aDevice){
    DAOFactory factory = new DAOFactory();
    try {
      factory.getDeviceDAO().add(aDevice);
    }
    catch (DataAccessException ex) {
      ex.printStackTrace();
    }
  }

}
 

To pile on even more interfaces, one variation on the above is to make an abstraction or the factory itself: the factory is defined as an interface, with methods similar to the above. Then, two concrete implementations of the factory interface are defined, one for each kind of datastore.

package myapp;

public interface Factory {
  
  DeviceDAO getDeviceDAO();
  
  UserDAO getUserDAO();
}
 



package myapp;

public final class FactoryBuilder {
  
  /** returns a specific implementation of Factory */
  Factory getFactory(){
    //elided
    return null;
  }

}
 



See Also :
Factory methods
Data access objects
Data exception wrapping
Construct Object using class name
Minimize ripple effects
Parse parameters into domain objects
Would you use this technique?
Yes   No   Undecided   
© 2014 Hirondelle Systems | Source Code | Contact | License | RSS
Individual code snippets can be used under this BSD license - Last updated on September 21, 2013.
Over 2,000,000 unique IPs last year - Built with WEB4J.
- In Memoriam : Bill Dirani -