11.4. Indirection

 <  Day Day Up  >  

An oft-quoted rule of computer science is "Most problems in computer science can be solved by another level of indirection ." One might complain that too many levels of indirection can create maintenance problems. But like many other design facets, taking out indirection is often easier than adding it later. The Factory pattern (see Design Patterns by Erich Gamma et al.) is a common form of indirection. Instead of creating an object directly with a constructor, you obtain the object from a Factory class method. This method hides the actual implementation of the object's creation from the caller (see Example 11-7).

Example 11-7. ZipCodeVerificationServiceFactory
 enum ZipCodeVerificationServiceType {NORMAL, TRACKING}; class ZipCodeVerificationServiceFactory     {     static ZipCodeVerificationService get_instance         (ZipCodeVerificationServiceType type_to_instantiate);         {         switch(type_to_instantiate)             {         case NORMAL:             return new ZipCodeVerificationImplementation( );             break;         case TRACKING:             return new ZipCodeVerificationTracker( );             break;         }     } 

We alter the initialization of zip_code_verification in Example 11-4 to call the factory get_instance( ) method with the type requested ( NORMAL or trACKING ):

 static ZipCodeVerificationService zip_code_verification =         ZipCodeVerificationServiceFactory.get_instance(TRACKING); 

Note that the caller can choose his desired implementation type, but does not need to specify the name of the actual implementation. The caller specifies the what , but not the how .

If the decision to use logging should be made at execution time rather than compile time, the factory can read the type value from a configuration file. The factory code is changed, as shown in Example 11-8.

Example 11-8. ZipCodeVerificationServiceFactory with configuration
 enum ZipCodeVerificationServiceType {NORMAL, TRACKING}; class ZipCodeVerificationServiceConfigurationDTO     {     ZipCodeVerificationServiceType service_type;     } class ZipCodeVerificationServiceFactory     {     static ZipCodeVerificationService get_instance( );     static ZipCodeVerificationServiceConfigurationDTO read_configuration( );     } ZipCodeVerificationService get_instance( )     {     ZipCodeVerificationServiceConfigurationDTO configuration =         read_configuration( );     switch(configuration.service_type)         {     case NORMAL:         return new ZipCodeVerificationImplementation( );         break;     case TRACKING:         return new ZipCodeVerificationTracker( );         break;         }     } 

In addition to using configuration information, the factory could check the environment in which the program is running and instantiate a logging class appropriate to that environment. In this example, the environment might permit an Internet connection. If so, the factory could instantiate a logging implementation that used a remote server.

Note that the configuration information in Example 11-8 has been removed by one level of indirection from the format of the configuration file. The read_configuration( ) method returns an object containing all the configuration information that ZipCodeVerificationServiceFactory requires.

WHEN IN DOUBT, INDIRECT

Indirection, using either methods or data, adds flexibility .


To avoid ZipCodeVerificationServiceFactory depending on a configuration file, we can alter the design to use Dependency Injection. Martin Fowler describes Dependency Injection (also called Inversion of Control) in detail at http://martinfowler.com/articles/injection.html. The user of a class injects data on which an object depends when the object is created. The dependencies are defined through constructor arguments, arguments to a factory method, or settable properties. The code using Dependency Injection appears in Example 11-9.

Example 11-9. ZipCodeVerificationServiceFactory Dependency Injection
 class ZipCodeVerificationServiceFactory     {     static ZipCodeVerificationService get_instance(         ZipCodeVerificationServiceConfigurationDTO configuration);     } ZipCodeVerificationService get_instance(         ZipCodeVerificationServiceConfigurationDTO configuration)     {     switch(configuration.service_type)         {     case NORMAL:         return new ZipCodeVerificationImplementation( );         break;     case TRACKING:         return new ZipCodeVerificationTracker( );         break;         }     } 

INDIRECTION WITH CSS

The original Hypertext Markup Language (HTML) provided tags that represented the text's display format. For example, italicized text was surrounded by <I> and </I> tags. These tags made the text appear in italics, but didn't capture the rationale for why the text should be italicized. The Cascading Style Sheet (CSS) allows you to create classes to which format is assigned elsewhere. For example, you can have a class named ForeignWords , in which case text would be surrounded by <div class="ForeignWords"> and </div> . The actual display format is described in a stylesheet. Classifying text keeps information regarding the reason for the format. The indirection through the stylesheet makes it easy to change the appearance of all text of a single class.


 <  Day Day Up  >  


Prefactoring
Prefactoring: Extreme Abstraction, Extreme Separation, Extreme Readability
ISBN: 0596008740
EAN: 2147483647
Year: 2005
Pages: 175
Authors: Ken Pugh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net