3

I'm reading about "Factory method" design pattern from "Head First Design Patterns". So, there is a class

public class PizzaStore {

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {

        Pizza pizza;

        pizza = factory.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }
}

As you can see above, we store factory and then call createPizza(String) virtual function and this will call appropriate createPizza(...) function for the concrete factory class, e.g., NYStylePizzaFactory or ChicagoStylePizzaFactory. But then author continues:

But you’d like a little more quality control...!

and does the following changes in the code:

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = createPizza();
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    abstract Pizza createPizza();
}

public class NYStylePizzaStore : PizzaStore {
    Pizza createPizza() {
        return new NYStylePizza();
    }
}

public class ChicagoStylePizzaStore : PizzaStore {
    Pizza createPizza() {
        return new ChicagoStylePizza();
    }
}

So, I don't understand why we gain more quality control in the latter case? It seems that the result is the same - appropriate virtual function is called.

  • 1
    Why the down-vote? This question is not about explaining how code works, but explaining the concepts behind the code. If that's not allowed, we basically have to down vote every question on the site. – Greg Burghardt Sep 22 '17 at 16:00
  • 1
    If you can move a correctness guarantee to compile time, using strong typing, those solutions are almost always better. – Frank Hileman Sep 22 '17 at 16:53
  • Altought Algorithm are PL. What PL are you using for the example ? – umlcat 13 hours ago
1

I don't have the book but I found some awesome slides about this topic. I will assume that the slides follow the book. They seem to confirm my initial guess that this is about control over pizza creation.

In the first example the store delegates pizza creation to the factory, which is injected from outside by the creating code. The store itself has no say on how pizzas are created, it just asks for them to the factory.

In the second example the store controls pizza creation via createPizza.

Now you ask "[i]sn't it more logical that concrete factory should create a pizza and not the store?"?

Note that in the second example we have a factory too, only it is a method instead of an object. So let me reword the question:

Isn't it more logical to use an injected factory instead of a factory method?

Maybe so. My slides suggest that you as the franchiser want to give the stores control over pizza creation while retaining control over order preparation, which makes sense, and the bit about quality control seems to refer to this.

But from there it jumps to "what you'd really like to do is create a framework that ties the store and the pizza creation together" and I can't see why.

It is true that an abstract base class implementing orderPizza and subclasses implementing createPizza is the more straightforward model of the above and this gives the author a good excuse to introduce the factory pattern. But the map is not the territory and software designers have different concerns than franchisers and franchisees of pizza stores. It is not necessarily true that the more straightforward model is the best one and control in the real world is not the same as control in OO software.

  • If Greg's runtime error case is not accurate, in that case I don't understand (again) difference between creation of pizza delegated to the concrete factory and inherited store taken that responsibility. – David Hovsepyan Sep 22 '17 at 18:46
  • @DavidHovsepyan The difference is who is in control of how pizzas are made. Is it not clear enough in the answer? Or you don't understand what it means? Or you don't understand why it could matter at all? – Goyo Sep 22 '17 at 19:05
  • The last one - why it matters. Isn't it more logical that concrete factory should create a pizza and not the store? Maybe concrete problem's context will clarify that aspect? – David Hovsepyan Sep 22 '17 at 19:09
  • @DavidHovsepyan See my edit. After some research I don't think that good reasons for using the factory method are provided. – Goyo Sep 22 '17 at 23:03
  • Awesome effort! Great slides. Thank you very much, now I got the difference. – David Hovsepyan Sep 23 '17 at 10:18
7

It's a difference between checking the correctness of something at run time (example #1) or compile time (example #2).

The first example requires you to pass around a string to determine the type of pizza, which can introduce the possibility of runtime errors:

PizzaStore store = new PizzaStore();
Pizza pizza1 = store.orderPizza("chicago style"); // Seems right
Pizza pizza2 = store.orderPizza("blue jeans");    // Blatantly wrong, but compiles

The second pizza type will probably not be understood by the pizza factory (who ever heard of a "blue jeans" pizza?) and at that point the pizza factory will throw an exception.

The second example verifies the correctness of your pizza types at compile time because you are using classes and inheritance for both the pizza stores and pizza styles.

  • The second example avoids the problem you described by ignoring the pizza type in the order. Sure, this allows clients to order any kind of pizza without risking a crash, but looks dumb to me. And this could have been done with composition too, the change to inheritance is unrelated. – Goyo Sep 22 '17 at 16:33
  • My comment was about the main point in your answer, not the main point in the code examples. Actually I agree with you, the handling of the pizza type string is not the main point of the examples. – Goyo Sep 22 '17 at 17:01
  • @Goyo: Composition would work if you use the Strategy Pattern where a concrete strategy object (or factory) returns a concretype... but then inheritance is in the picture again, so the answer still stands. – Greg Burghardt Sep 22 '17 at 17:05
  • @Goyo: And I still think the main point is to differentiate run time versus compile time checks. The first example using a string parameter is a run time check. The second example is a compile time check. Clarified my answer. – Greg Burghardt Sep 22 '17 at 17:07
  • Composition would work the same (again, regarding the main point in your answer) just by removing the type argument to SimplePizzaFactory.createPizza. Note that your claim "The second example verifies the correctness of your pizza types at compile time" is inaccurate if it refers to the string in the order. It is not verified at any time, just ignored. – Goyo Sep 22 '17 at 17:12
1

First, let me quote the complete paragraph from P109 of the book:

" But you’d like a little more quality control...!

So you test marketed the SimpleFactory idea, and what you found was that the franchises were using your factory to create pizzas, but starting to employ their own home grown procedures for the rest of the process: they'd bake things a little differently, they'd forget to cut the pizza and they'd use third-party boxes."

Below is my understanding to this paragraph, that is, why did the author claim that the 'quality control" could be any issue if we were to use a Simple Factory pattern in this case.

Imagine the owner of one franchise decides not to pay the franchise fee anymore, and would like to turn it into a knock-off store, i.e., from a pizza hut to a pizza hat.

Previously, he has been doing (because he is located in NYC):

NYStylePizzaStore nyFactory = new NYPizzaFactory();
PizzaStore nyStore = new PizzaStore(nyFactory);
Pizza pizza = nyStore.order("Veggie") // This is a good veggie pizza to eat!

Now, he decides to do the following:

NYStylePizzaStore nyFactory = new NYPizzaFactory();
Pizza pizza = nyfactory.createPizza("Veggie")
pizza.box() // why are we boxing a raw pizza?
pizza.cut() // and now we're cutting the entire box?
pizza.bake() // and bake it?

Notice that he is not doing things in the right order! And the customer would end up getting a oily box containing a burned pizza. What's worse, the customer thought that they are getting the pizza from a legit pizza hut (He never noticed the store name has been secretly changed to pizza hat!). This would totally ruin the reputation of your franchising business.

I believe the point here is that we should properly encapsulate the factory logic, instead of exposing it to the general public, by using the Factory Method Pattern. That would help improve the "quality control" problem, because no one can easily add his own "improvements" to the PizzaStore procedure anymore (He would not have access to an unbaked, raw pizza at the first place.)

In other words, you should only get to use the pizza factory if you are paying the franchise fee and being a legit franchise owner. The pizza creation procedure and the pizza making procedure should really be tied together.

New contributor
yuanlu0210 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.