Search code examples
design-patternsfactory-pattern

Factory Method pattern - what for this pattern is necessary?


I have abstract class-factory Factory with factory-method getProduct() and his child classes.

I have abstract class-product Product and his child classes.

Classes-factories created objects of classes-products.

abstract class Factory {
    abstract function getProduct();
}
class FirstFactory extends Factory {
    public function getProduct() {
        return new FirstProduct();
    }
}

abstract class Product {
};
class FirstProduct extends Product {
}

In result I can use this client code:

$factory = new FirstFactory();
$firstProduct = $factory->getProduct();
$factory = new SecondFactory();
$secondProduct = $factory->getProduct();

Question: what for this pattern is necessary? Because in client code I just can use direct classes:

$firstProduct = new FirstProduct();
$secondProduct = new SecondProduct();

Solution

  • It's important to note that the Factory Pattern is not always the best solution and for simple case, it is even better having simple new. For instance, your example would probably be better without Factory. Factory Pattern takes all its power when you don't need to work on the exact implementation of a class but stay at the 'abstraction layer' (ex. Interface and abstract classes). You can have a look at the "Dependency Inversion Principle DIP" (Depend upon abstraction. Do not depend upon concrete classes).

    Factory use case: switching Database with one line

    For instance, let's say you have a software that use Database system. For whatever reason, you know the concrete database used (MongoDB, SQL...) may change later. (Or even just need a fake hard coded database in files during the development). The factory pattern allows you to switch from one to another in just one line, by calling the right Factory, since all the implementation depends upon abstraction. (This is actually the DAO pattern that makes a great use of Factory Pattern, see the oracle documentation for more informations: http://www.oracle.com/technetwork/java/dataaccessobject-138824.html)

    Concrete example: Game with 2 Factions

    Here a concrete and simple example of implementation.

    You have 2 Units

    • Peon
    • Warrior

    You have 2 Factions

    • Orc
      • Orc Peon
      • Orc Warrior
    • Human
      • Human Peon
      • Human Warrior

    2 Players

    • Orc player (Use only Orc units)
    • Human player (Use only Human units)

    You want to instantiate both players but the concrete player class should be implemented in the more generic way, so that it can be reused later. This is also really important in case of adding several new faction, you don't want to spend time going back to your player class.

    The code sample

    To build and run it, copy in Main.java, then javac Main.java and java Main

    The result should be

    enter image description here

    // Factories ----------------------------------------
    
    abstract class AbsUnitFactory {
        public abstract Warrior creaWarrior();
        public abstract Peon creaPeon();
    }
    
    class OrcFactory extends AbsUnitFactory {
        public Warrior creaWarrior() {
            return new OrcWarrior();
        }
        public Peon creaPeon() {
            return new OrcPeon();
        }
    }
    
    class HumanFactory extends AbsUnitFactory {
        public Warrior creaWarrior() {
            return new HumanWarrior();
        }
        public Peon creaPeon() {
            return new HumanPeon();
        }
    }
    
    abstract class Unit {
        public abstract String getRole();
        public abstract String getFaction();
    
        @Override
        public String toString() {
            String str = new String();
            str += "[UNIT]\n";
            str += "    Role: " + this.getRole() + "\n";
            str += "    Faction: " + this.getFaction() + "\n";
            return str;
        }
    }
    
    
    // Warrior Units ----------------------------------------
    
    abstract class Warrior extends Unit {
        @Override
        public String getRole() {
            return "I'm a badass Warrior with the biggest sword!";
        }
    }
    
    class OrcWarrior extends Warrior {
        @Override
        public String getFaction() {
            return "Orc";
        }
    }
    
    class HumanWarrior extends Warrior {
        @Override
        public String getFaction() {
            return "Human";
        }
    }
    
    
    // Peon Units ----------------------------------------
    
    abstract class Peon extends Unit {
        @Override
        public String getRole() {
            return "I'm a little simple peon... Ready to work.";
        }
    }
    
    class HumanPeon extends Peon {
        @Override
        public String getFaction() {
            return "Human";
        }
    }
    
    class OrcPeon extends Peon {
        @Override
        public String getFaction() {
            return "Orc";
        }
    }
    
    
    // Main components ----------------------------------------
    
    class Player {
        private AbsUnitFactory  factory;
        private Peon            myPeon;
        private Warrior         myWarrior;
    
        public Player(AbsUnitFactory pFactory) {
            this.factory    = pFactory;
            this.myPeon     = this.factory.creaPeon();
            this.myWarrior  = this.factory.creaWarrior();
        }
    
        @Override
        public String toString() {
            return this.myPeon.toString() + this.myWarrior.toString();
        }
    }
    
    class Main {
        public static void main(String[] args) {
            AbsUnitFactory  humanFactory    = new HumanFactory();
            AbsUnitFactory  orcFactory      = new OrcFactory();
            Player          humanPlayer     = new Player(humanFactory);
            Player          orcPlayer       = new Player(orcFactory);
    
            System.out.println("***** Human player *****");
            System.out.println(humanPlayer.toString());
    
            System.out.println("***** Orce player *****");
            System.out.println(orcPlayer.toString());
        }
    }
    

    See how the player class can be reused for any faction and the only line that defines witch faction to use is the Factory. (You may even add a singleton).

    More resources

    These are books I really appreciate (About Design Patterns)