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();

Upvotes: 0

Views: 387

Answers (3)

geekymoose
geekymoose

Reputation: 394

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)

Upvotes: 1

David Guida
David Guida

Reputation: 1155

a Factory can be injected instead of the actual class. Suppose you have a class that can be instantiated only at runtime based on some specific conditions, in this case you cannot do new Foo(...args) . One option is to inject FooFactory instead and have it create the Foo instance for you.

Upvotes: 0

Chris Drew
Chris Drew

Reputation: 15334

If you know at compile time that firstProduct is always of type FirstProduct and secondProduct is always of type SecondProduct then there is no need for an factory method.

A factory method is only useful if you want to create a product that might be FirstProduct or might be SecondProduct depending on the runtime type of a factory. Perhaps, for example, the type of the factory is decided by user input.

Upvotes: 1

Related Questions