Friday, September 30, 2011

Java - Factory Method Concept

Factory method is just a fancy name for a method that instantiates objects. Like a factory, the job of the factory method is to create -- or manufacture -- objects.
Let's consider an example.
Every program needs a way to report errors. Consider the following interface:
Listing 1
public interface Trace {
      // turn on and off debugging
      public void setDebug( boolean debug );
      // write out a debug message
      public void debug( String message );
      // write out an error message
      public void error( String message );
}


Suppose that you've written two implementations. One implementation (Listing 2) writes the messages out to the command line, while another (Listing 3) writes them to a file.
Listing 2
public class FileTrace implements Trace {
          
      private java.io.PrintWriter pw;
      private boolean debug;
      public FileTrace() throws java.io.IOException {
            // a real FileTrace would need to obtain the filename somewhere
            // for the example I'll hardcode it
            pw = new java.io.PrintWriter( new java.io.FileWriter( "c:\trace.log" ) );
      }
      public void setDebug( boolean debug ) {
            this.debug = debug;
      }
      public void debug( String message ) {
            if( debug ) {  // only print if debug is true
                  pw.println( "DEBUG: " + message );
                  pw.flush();
            }
      }
      public void error( String message ) {
            // always print out errors
            pw.println( "ERROR: " + message );
            pw.flush();
      }
}


Listing 3
public class SystemTrace implements Trace {
      private boolean debug;
      public void setDebug( boolean debug ) {
            this.debug = debug;
      }
      public void debug( String message ) {
            if( debug ) {  // only print if debug is true
                  System.out.println( "DEBUG: " + message );
            }
      }
      public void error( String message ) {
            // always print out errors
            System.out.println( "ERROR: " + message );
      }
}


To use either of these classes, you would need to do the following:
Listing 4
//... some code ...
SystemTrace log = new SystemTrace();
//... code ...
log.debug( "entering loog" );
// ... etc ...


Now if you want to change the Trace implementation that your program uses, you'll need to edit each class that instantiates a Trace implementation. Depending upon the number of classes that use Trace, it might take a lot of work for you to make the change. Plus, you want to avoid altering your classes as much as possible.
A factory method lets us be a lot smarter about how our classes obtain Trace implementation instances:
Listing 5
public class TraceFactory {
      public static Trace getTrace() {
            return new SystemTrace();
      }
}


getTrace() is a factory method. Now, whenever you want to obtain a reference to a Trace, you can simply call TraceFactory.getTrace():
Listing 6
//... some code ...
Trace log = TraceFactory.getTrace();
//... code ...
log.debug( "entering loog" );
// ... etc ...


Using a factory method to obtain an instance can save you a lot of work later. In the code above, TraceFactory returns SystemTrace instances. Imagine again that your requirements change and that you need to write your messages out to a file. However, if you use a factory method to obtain your instance, you need to make only one change in one class in order to meet the new requirements. You do not need to make changes in every class that uses Trace. Instead you can simply redefine getTrace():

Implementation:

package pkg;

interface Bicycle {

    //void changeCadence(int newValue);   // wheel revolutions per minute

    //void changeGear(int newValue);

    //void speedUp(int increment);

    void applyBrakes(int decrement);

}


class ACMEBicycle implements Bicycle {

       // remainder of this class implemented as before
    public void applyBrakes(int decrement)
    {
        System.out.println(decrement +  " Breaks applied");
    }
   

    }


class BicycleFactory {
    public static Bicycle getBicycle() {
          return new ACMEBicycle();
    }
}

public class HelloWorld {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //System.out.println("Hello Eclipse!");       
   
        /*
        ACMEBicycle oACMEBicycle;
        oACMEBicycle = new ACMEBicycle();
        oACMEBicycle.applyBrakes(7);
        */
       
   
   
    Bicycle b = BicycleFactory.getBicycle();
    b.applyBrakes(10);
       
    }

}


If we want to have implementation in run time, the we can remove ACMEBicycle class from above code and use  reflection in factory method "getBicycle()". Reflection is a powerful feature of java to call class and it methods as string input.

No comments:

Post a Comment