Software Design Patterns

Factory Method Pattern

Factory Method(107) Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation (of Products) to subclasses. (Gof)

Why would we want use Factory Method?

  • A class can't anticipate the class of objects it must create.
  • A class wants its subclasses to specify the objects it creates.
  • Classes delegate responsibility to one of several helper subclasses, and you want to localize the knowlege of which helper subclass is the delegate.(Gof.)

    This pattern name seems to be abused more than most. You are sure to hear many people say that they are using a Factory Pattern or Factory Method or Factory Method Pattern just because they have a class with a function that returns an object. That is not the definition of a Factory Method. That is just a function with a return type.

    Whats the difference?

    The Factory Method Pattern really has one key specification: "lets a class defer instantiation (of Products) to subclasses".  The key concept to remember is that we have subclasses that will each implement the 'factory method' so that base class doesn't know which concrete object is going to be created or how it is going to be created.

    If you've read (Gof) then you've seen an example of an Application object with a CreateDocument() factory method. Each subclass of Application can implement the CreateDocument() factory method to return a different subclass of Document.

    You have a also seen the example with Mazes where different subclasses of MazeGame (BombedMazeGame, EnchantedMazeGame) return different types of Mazes.

    How to create objects:

    I want to show another use of the Factory Method Pattern,   and this is the case of using the Factory Method Pattern to separate the logic of not only which product to create, but how to create the objects.

    In this scenario, we have parallel class hierarchies, or partially paralell class hierarhies.

    parallelClsHier.gif

    We are going to use Dedicated Factory Classes. Unlike the samples in (Gof), these classes really have no purpose in life except to build 1 particular Concrete Product object. 


    Dedicated Factory Classes don't do anything except create one particular Concrete Product. (and btw, yes I made up the name, Dedicated Factory Classes, to allow us to have some terminology for this subset of Factory Method Pattern)

    Why use this approach, it seems crazy because we have to have a separate class to match each class of product, and it seems that a factory should produce more than one product?  Because the concrete product we are building requires a complex algorithm to build it. The logic is very similar to why you would use the Strategy Pattern to encapsulate algorithms.

    • We don't want clients to repeat the complex algorithm required to build the concrete product.
    • Products don't need to know how they are created.
    • Some (or all) concrete products can be created in multiple ways, or we want to leave open the option that in the future there may be new ways to create the concrete product.

    Factory Methods calling Factory Methods

    Scenario:

    I wrote an application to screen sales transactions for indications of credit card fraud. To do this, the customer's information including thier IP address, credit card information, and billing information is checked against known patterns (not software design patterns) used by credit card fraudsters. A series of 'risk factors' are created and a score is given for each one. If the overall total score is too high, the transaction is denied automatically. This saves hours of time reversing sales that never should have occured in the first place.

    In this application, we have 3 different modes that can be used when creating the 'risk factor' collections:

    1. Demo - returns a collection of risk factors with demo data.
    2. Standard - returns a collection of risk factors with real data
    3. Premium - same as Standard, but checks and returns additional risk factors.

    As you can see, we always need to create a collection of risk factors, but we do it differently depending on the mode being used. Looking back at our reasons for possibly using 'Factory Method', which reasons apply here?

    • 'A class can't anticipate the class of objects it must create' - I actually can't anticipate a collection of classes to create, until the mode is known.
    • 'A class wants its subclasses to specify objects to create' - I want to do this to make the code modular and easier to adapt to change.
    • 'Classes delegate responsibility to one of several subclasses, and you want to localize the knowledge of which helper subclass is the delegate' - I'm going to have my factory method, 'CreateRiskFactors()', call the factory method of each RiskFactor type, so I meet this condition multiple times.

    You don't need to meet all these conditions to use the 'Factory Method'. If you meet any of these conditions you may want to use the 'Factory Method'.

    If you know that the 'Factory Method' is being used, you should expect an interface or an abstract class that defines a function to get (or create) an object(s). Since interfaces and abstract classes are not implemented, we know we will have derived classes, and we expect the derived classes to contain the implementation of returning the object.

    * Important Concept: If the resposibilty of getting (or creating) the return object is not handled in a subclass, then you are NOT using the 'Factory Method' pattern.

    In the following example we have an abstract class, DetailedRiskScore, with a factory method called CreateRiskFactors(). I hope the fact that we are returning a collection of objects doesn't cause much confusion. As we expect, the base class doesn't care what collection of objects are returned. This will work for our needs regardless of the mode (Demo, Standard, Premium).

    Each subclass of DetailedRiskScore implements the factory method,CreateRiskFactors(), and determines what actually gets returned.

    This is very similar to the samples in (Gof), but it doesn't just instatiate concrete products, it calls multiple Factory Methods called CreateRiskFactor() in our Dedicated Factory Classes., which are all subclasses of Factory_RiskFactor.                

        

        abstract class DetailedRiskScore

        {

            public abstract ArrayList CreateRiskFactors();         

        }

     


        class Standard_DetailedRiskScore : DetailedRiskScore

        {

            public ArrayList CreateRiskFactors()

            {

                ArrayList a = new ArrayList();

                a.Add = new EmailFactory().CreateRiskFactor();

                a.Add = new IPFactory().CreateRiskFactor();

     

                return a;

            }

        }

     

        class Demo_DetailedRiskScore : DetailedRiskScore

        {

            public ArrayList CreateRiskFactors()

            {

                ArrayList a = new ArrayList();

                a.Add = new Demo_EmailFactory().CreateRiskFactor();

                a.Add = new Demo_IPFactory().CreateRiskFactor();

     

                return a;

            }

        }

     

        class Premium_DetailedRiskScore : DetailedRiskScore

        {

            public ArrayList CreateRiskFactors()

            {

                ArrayList a = new ArrayList();

                a.Add = new EmailFactory().CreateRiskFactor();

                a.Add = new IPFactory().CreateRiskFactor();

     

                //premium so we also check the AnonProxyRisk

                a.Add = new AnonProxyFactory().CreateRiskFactor();

     

                return a;

            }

        }

     


    Client Code:

        class Client

        {

            public void GetRiskFactors(string Mode) 

            {   

                DetailedRiskScore drs;

                ArrayList risks;

     

                if(Mode == "demo")

                   drs = new Demo_DetailedRiskScore();

     

                else if(Mode == "standard")

                   drs = new Standard_DetailedRiskScore();

     

                else if(Mode == "premium")

                   drs = new Standard_DetailedRiskScore();

     

                for(int i = 0; i < risks.Count; i++)

                {

                   Response.Write(risks[i].Name + " " + risks[i].Points;

                }            

            }

        }

    To understand why we would want to go through all the trouble of using the Factor Method pattern, let's review some advantages that can be seen in the above example.

    1. The client only needs to know which mode to use, and the client is shielded from making further decisions about how to construct the risk factors.
    2. Regardless of which mode is used, after the object is created, the client only needs to know about 2 types of objects which are our base types DetailedRiskScore and RiskFactor.
      • Don't care if DetailedRiskScore is Demo,Standard, or Premium.
      • Don't care if RiskFactor is Email, IP, or AnonymousProxy.
    3. If we need to modify the way a DetailedRiskScore's factory method is implemented, such as we need to add a new type of RiskFactor, our client code will not break. It will be able to continue working if we add and remove RiskFactors.
    4. If we need to add a 4th type of DetailedRiskScore, we ONLY need to add another 'if' statement in our client, and we can most likely reuse many of our existing RiskFactors in our new DetailedRiskScore subclass.

    In each subclass of DetailedRiskScore we implement the factory method CreateRiskFactors(). These subclasses decide which objects to create, and they do this by calling the factory methods, CreateRiskFactor()  of our Dedicated Factory Classes, and these factory methods hide a complex algorithms to create a concrete products. Each subclass overrides CreateRiskFactor() to decide which product is created and how that subclass is created.


    Another (not so real, but easy to follow) example

    In the following example we have an abstract class CarFactory. We expect car factories to output cars. The process for putting a car together is always the same, but each car factory will use different parts to create it's cars. It may build it's own parts and it may get parts from other more specialized factories.

    Let's say we have a factory that makes car parts and outputs them (returns them as output). In the car factory it may send this output to a assembly line or it may send the output to inventory to be sold. Let's look at it:

        abstract class CarFactory

        {

            //All 6 of these are Factory Methods because

            // subclasses will decide which objects create and

            // how to create them

     

            //Things a car factory can produce itself

            public abstract Door CreateDoor();

            public abstract Hood CreateHood();

            public abstract TrunkLid CreateTrunkLid();

     

            // more complex things we should get from

            // a dedicated factory for producing that one object

            public abstract Radio CreateRadio();

            public abstract Lightbulb CreateLightbulb();

            public abstract Computer CreateComputer();

     

     

        }

     

        class FordMustangFactory : CarFactory

        {

            public Door CreateDoor(){

                return new MustangDoor();}

     

            public Hood CreateHood(){

                return new MustangHood(); }

     

            public TrunkLid CreateTrunkLid(){

                return new MustangTrunkLid(); }

     

            public Radio CreateRadio(){

                return new AlpineStereoFactory().CreateRadio();}

           

            public Lightbulb CreateLightbulb() {

                return new SylvaniaLightBulbFactory().CreateLightBulb();}

           

            public Computer CreateComputer(){

                return new AAAComputerFactory().CreateComputer(); }

     

        }

     

        class ChevroletCapriceFactory : CarFactory

        {

            public Door CreateDoor(){

                return new CapriceDoor();}

     

            public Hood CreateHood(){

                return new CapriceHood();}

     

            public TrunkLid CreateTrunkLid(){

                return new CapriceTrunkLid();}

               

            public Radio CreateRadio(){

                return new KenwoodStereoFactory().CreateRadio(); }

     

            public Lightbulb CreateLightbulb(){

                    return new SylvaniaLightBulbFactory().CreateLightBulb();}

     

            public Computer CreateComputer(){

                    return new AAAComputerFactory().CreateComputer();}

        }

    Some products are created by the car factory, and other products are more complex and best left to specialized factories (think Dedicated Factory Classes). In this example, car factories can create metal body panels for thier particular cars, but there are specialized parts made by other factories, such as radios, light bulbs, and computers.

        class AlpineStereoFactory : StereoFactory

        {

            public Radio CreateRadio()

            {

                Radio r = new Radio();

                r.Type = "Alpine";

                return r;

            }

        }

        class KenwoodStereoFactory : StereoFactory

        {

            public Radio CreateRadio()

            {

                Radio r = new Radio();

                r.Type = "Kenwood";

                return r;

            }

        }

        class SylvaniaLightBulbFactory : LightBulbFactory

        {

            Lightbulb CreateLightBulb()

            {

                Lightbulb l = new Lightbulb();

                l.Type = "Sylvania bulb";

                return l;

            }

        }  

        class AAAComputerFactory : ComputerFactory

        {

            public Computer CreateComputer()

            {

                Computer c = new Computer();

                c.Type = "AAA Computer";

                return c;

            }

        }

     

    It is easy to see that we not only benefit by separating (encapsulating) complex logic, but we also get to re-use factories. Two different car factories can use the same radio factory, but they can just easily use different radio factories.

    Why do I care? CHANGE: The thing that haunts all software developers.  If you don't design your code to be prepared for change, then change will be much more difficult.

    Car expects a 'radio' but it doesn't really care what radio we use ,or what factory we got it from, or how it was built. We can easily change our algorithm to call another factory and get a different radio for our car. We can add new radio factories and get radios from them, and we can let other car factories get radios from existing factories. Different radio factories return radios that are constructed (quite complexly) in different ways, but they always match the 'radio' interface so we don't care how its created, just that its a radio.

    Even though radios are complex, there creation is invisible to the client code so client code has no dependency on how they are put together, and we can make changes to radios without causing breaking changes to the rest of our code. This is loose coupling because cars are not dependent on any particular radio or the way it is assembled. This is high cohesion because we have radio factories that do a very specific job and car factories do a very specific job (not building radios, lightbulbs, and computers).

    Quick Summary

    Is this a Factory Method?

        class CustomDataAdapter

        {

            public IDbConnection GetConnection()

            {

                return new System.Data.SqlClient.SqlConnection();

            }

        }

    No. A factory method lets the subclasses decide which product to produce, and how to produce it, so the following is a Factory Method.

        abstract class CustomDataAdapter

        {

            public abstract IDbConnection GetConnection();

        }

        class SqlDataAdapter : CustomDataAdapter

        {

            public override IDbConnection GetConnection()

            {

                return new System.Data.SqlClient.SqlConnection();

            }

        }

        class OdbcDataAdapter : CustomDataAdapter

        {

            public override IDbConnection GetConnection()

            {

                return new System.Data.Odbc.OdbcConnection();

            }

        }

    It is the same function, and it does the same thing except that subclasses are now responsible for returning the correct object (concrete product).

    0 Comments