4

I know there are hundreds of questions about this on here. I've probably read fifty different questions, blog posts and textbooks. The problem is I've gotten about 75 different answers. I have seen all of the following described as some type of factory, and I can't follow the muddled terminology, and get confused on the pros and cons.

public class PizzaFactory {
  public static Pizza(String city) {
    switch(city) {
      case "Chicago": return new DeepDishPizza();
      case "New York": return new ThinCrustPizza();
    }
  }
}

vs

  public abstract class PizzaFactory {
    public Pizza getPizza();
  }

  public class ChicagoPizzaShop extends PizzaFactory {
    public Pizza getPizza() {
      return new DeepDishPizza();
    }
  }

  public class NewYorkPizzaShop extends PizzaFactory {
    public Pizza getPizza() {
      return new ThinCrustPizza();
    }
  }

public class SomeOtherClass {
  public Pizza getPizza(String city) {
    PizzaFactory pizzaShop;
    switch(city) {
      case "Chicago": pizzaShop = new ChicagoPizzaShop();
        break;
      case "New York" : pizzaShop = new NewYorkPizzaShop();
        break;
    return pizzaShop.getPizza();
    }
  }
}

vs

public class PizzaFactoryFactory {
  public abstract class PizzaFactory {
    public Pizza getPizza();
  }

  public class ChicagoPizzaShop extends PizzaFactory {
    public Pizza getPizza() {
      return new DeepDishPizza();
    }
  }

  public class NewYorkPizzaShop extends PizzaFactory {
    public Pizza getPizza() {
      return new ThinCrustPizza();
    }
  }

  public Pizza getPizza(String city) {
    switch (city) {
      case "Chicago": return new ChicagoPizzaShop().getPizza();
      case "New York": return new NewYorkPizzaShop().getPizza();
    }
  }
}

vs

public abstract class PizzaFactory {
    public Pizza getPizza();
  }

  public class ChicagoPizzaShop extends PizzaFactory {
    public Pizza getPizza() {
      return new DeepDishPizza();
    }
  }

  public class NewYorkPizzaShop extends PizzaFactory {
    public Pizza getPizza() {
      return new ThinCrustPizza();
    }
  }
}

public abstract Customer {
  public Pizza orderPizza();
}

public ChicagoCustomer extends Customer {
  public Pizza orderPizza() {
    return new ChicagoPizzaShop.getPizza();
  }
}

public NewYorkCustomer extends Customer {
  public Pizza orderPizza() {
    return new NewYorkPizzaShop.getPizza();
  }
}
// How does one choose which Customer to use? *Another* factory?

There are probably others I'm not thinking of right now. Which, if any, of these is an Abstract Factory pattern? The Factory Method pattern? Some other thing? More concretely, I have eight different subclasses of an abstract class, and I need to return one of those based on a user selection which I can represent either as a string, an int, or an enum. How to best go about it?

1 Answers1

8

Abstract factory only makes sense, when code that creates the factory is in completely different part of your application than the code that actually uses your factory. None of your examples actually represent this. Quite often, the factory is downstream the dependency between modules. So the code that consumes the created abstraction cannot possibly have dependency on the concrete implementations.

Following illustration shows where abstract factory makes sense.

Usage of abstract factory

This design has few good properties. When modules for concrete implementations(the ChicagoStores and NewYorkStores modules) change, it doesn't require recompilation of either other modules or the one that actually uses the implementations (People). Second, it allows you to add new concrete implementation (maybe WashingtonStores), after which you only need to change the "application" module, which binds everything together. The "composer" object is one that binds everything together. This would often be some kind of Inversion of Control framework. Also note, that the one that uses the factory to create instance needs multiple of those instances. If it only wanted single instance, then the composer can just pass in those instances. There would be no need to complicate the design with abstract factories.

But you might be saying : "Isn't this too complex?" Yes, that is correct. But if you have requirements that would force this kind of dependency structure, this kind of design is unavoidable. You need to weight the advantages you gain by separating the dependencies against complicated design.

One note for the end : If you are just learning design patterns, my personal opinion would be to simply ignore the (abstract) factory pattern. It's use is highly situational and it is more often abused then used properly. As many seem to agree with me and some more.

Also note, that what I described above is a design pattern called "Abstract Factory". There is also a pattern called "Factory" which IMO is just a method that creates objects. Your confusion probably stems from the fact that those two are often mixed together.

Euphoric
  • 38,149