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?

TBridges42
  • 343
  • 1
  • 7
  • 2
    Bonus question: How do I use any of the above to return a mock pizza when unit testing? – TBridges42 Jul 31 '15 at 04:08
  • 1
    Use factories when you are unable to know at compile-time what Type you need. Otherwise, just use new. – whatsisname Jul 31 '15 at 04:34
  • Your `getX()` methods are declared `void`, but return things. That's simply wrong. A factory is all about returning things, so it must declare a return type, probably the abstract `Pizza`. The entire point of factories is to be able to *use* a special-purpose type without *knowing* it. – Kilian Foth Jul 31 '15 at 06:26
  • @Kilian Foth, that's what I get for writing code at midnight without an IDE – TBridges42 Jul 31 '15 at 12:51

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
  • 36,735
  • 6
  • 78
  • 110
  • This isn't going to help a person who's new to factories. – Robert Harvey Jul 31 '15 at 04:28
  • @RobertHarvey Working on diagram right now – Euphoric Jul 31 '15 at 04:31
  • It may also be useful to mention that the "providers of the implementations" may be pluggable, and that the upstream provides a mechanism for downstream "providers" to register themselves as being able to create objects that satisfy some requirements (e.g. implements a certain interface and having certain behaviors.) – rwong Jul 31 '15 at 05:23
  • So your suggestion for my use case would be that I just have a method that switches over the user input and returns the correct implementation? – TBridges42 Jul 31 '15 at 12:48
  • Something like my first example? – TBridges42 Jul 31 '15 at 13:02
  • 1
    @TBridges42 Yes. That would be simplest solution. – Euphoric Jul 31 '15 at 16:34