Search code examples
javafactory-pattern

Why we need to pass the InterfaceFactory to get the object rather than just pass the specific object


There is one example of Interface and Factories in "Thinking In Java" as follows:

(1) Define the Game and GameFactory Interface

interface Game { boolean move(); }
interface GameFactory { Game getGame(); }

(2) Write their implementation

class Checkers implements Game {
     private int moves = 0;
     private static final int MOVES = 3;
     public boolean move() {
        System.out.println("Checkers move " + moves);
        return ++moves != MOVES;
     }
}

class CheckersFactory implements GameFactory {
    public Game getGame() { return new Checkers(); }
}

class Chess implements Game {
    private int moves = 0;
    private static final int MOVES = 4;
    public boolean move() {
       print("Chess move " + moves);
       return ++moves != MOVES;
    }
}

class ChessFactory implements GameFactory {
    public Game getGame() { return new Chess(); }
}

(3) And the client code

public class Games {
    public static void playGame(GameFatory factory) {
        Game s = factory.getGame();
        while (s.move());
    }
    public static void main(String[] args) {
        playGame(new CheckersFactory());
        playGame(new ChessFactory());
    }
}

So as for the client code, why do we bother to pass the GameFactory to the function of playGame?

Can we just set Game as the argument as follows:

public class Games {
    public static void playGame(Game game) {
        while (game.move());
    }
    public static void main(String[] args) {
        playGame(new Checkers());
        playGame(new Chess());
    }
}

So that there is no need for us to create a GameFactory to just get the Game

So I wonder what is the advantage of the Factory Method?


Solution

  • The Factory pattern exists to provide an object-oriented version of a functional programming idiom: providing a functor that generates values. Most often you see Factory combined with something like the Strategy pattern, where you have an interface that represents a way of doing something, and multiple concrete implementations of that interface that represent different ways to do that thing. The value of a Factory is that it abstracts the creation of a strategy.

    In practice, I find this useful in two situations:

    1. When client code needs to choose a strategy, but library code needs to control when and how often that strategy is used. This matches your Game example: a GameFactory represents a kind of game (chess, checkers, etc.), while a Game instance represents a particular game. If you were writing a class that played a tournament of matches between different players, you would need to construct it with a GameFactory to tell it what kind of game to play, but the Tournament itself would determine how many games to start and how to manipulate each game. Tournament can't just do new Chess() or new Checkers() because the goal is to make the Tournament class independent of the kind of game, so that it's reusable.

    2. Factory is also useful when creating a value is complex and you want essentially do partial application of the constructor. This approach often works well together with the Dependency Injection pattern. For example, consider that you now add a third kind of game, Poker. A Poker game needs a random-number generator so that it knows how to shuffle the cards. How do you fit that into your existing GameFactory/Game/Tournament structure? The PokerFactory object would take the random number generator as an argument, and it would be responsible for passing along the random number generator to any PokerGame objects it creates. A class that uses the GameFactory/Game API, like Tournament, wouldn't need to know that some games need a random number generator and some don't; that knowledge would be abstracted away.

    Taking a step back: like most OO design patterns, the whole purpose of the Factory pattern is to reduce coupling and increase cohesion. You don't want your Tournament class to be coupled to any particular game; decoupling the code makes it possible for the different parts of your program to change independently of each other.

    Taking an even farther step back: much of the knowledge and best practice around software engineering comes from large software projects, with so many lines of code that no human can remember everything that's going on everywhere, and with so many people working on the project that coordinating work between different parts of the program becomes one of the most important problems to solve. So many design patterns exist because they define "seams" between different parts of the programs to compartmentalize the concepts you need to hold in your head at one time. This allows two or more programmers to work on the different compartments at the same time, and makes it easier for you to ensure that each compartment is correct.

    In the kind of "toy" programs you write when learning a programming language or when studying programming for the first time, these tactics to compartmentalize a program may seem redundant or simply confusing. And indeed, if you apply these kinds of patterns unnecessarily then they actually make things worse, not better. But these techniques are "tools" to have in your toolbelt when you approach a large body of code: both so that you can better understand how the existing code is compartmentalized and so that you can compartmentalize the code you write.

    TL;DR: "Everything should be made as simple as possible, but no simpler."