简体   繁体   中英

Why is the factory method declared as protected?

I'm reading the Head First Design Patterns book and on the "Declaring a factory method" section in Chapter 4, the method is declared as protected:

public abstract class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare(); // other methods follow

        return pizza;
    }

    protected abstract Pizza createPizza(String type);

}

This confuses me because I initially thought, in fact it is also stated in the book, that having a factory (method) allows you to have a single place that creates an instance for you, not just for acting on later but also for "querying". By "acting on" I mean pizza.cut() etc, and by "querying" I mean pizza.isSpicy() .

Wouldn't the protected keyword limit the querying to only the subclasses and same-package classes? What if a 3rd-party class needed to know that the pizza is spicy before ordering?

I may be overthinking this, because the highlight box does not say it HAS to be protected but it's there in the sample code.

This confuses me because I initially thought, in fact it is also stated in the book, that having a factory (method) allows you to have a single place that creates an instance for you, not just for acting on later but also for "querying"

  • If you wanted the client (that is, the calling code) to be in control of when a Pizza gets "created" then you wouldn't make the createPizza method protected; it would be a public method and anyone with a reference to an instance of PizzaStore could call it.

  • If you think about that class, the semantics of orderPizza are clearly different from createPizza - the former deals with a caller asking for a resource, whereas the latter deals with obtaining that resource, whether this be creating a new one or re-using an old one.

  • So in this case, it's clear that the PizzaStore class wants to retain control over when a pizza actually gets "created", and this is why it's protected. (also because a class that inherits from PizzaStore can also implement the method - if it were private it couldn't see it in order to implement it - and note that since it's abstract, a subclass has to implement it). So the creation process is (as Ananthu also mentioned) safely encapsulated within the store.

Let's suppose that the actual "creation" of a Pizza is a really expensive operation; we don't want just anyone to decide when it happens. Therefore by keeping the factory method protected, it allows the PizzaStore to make decisions such as (pseudo-code, I don't know Java) :

public abstract class PizzaStore {

    protected PizzaBin bin;

    //customer orders a pizza
    public Pizza orderPizza(String type) {
        //maybe we already have one...
        Pizza pizza = HeresOneWeMadeEarlier(type);
        if (pizza !=null) return pizza;
        //nope, we have none, so we have to make one.
        pizza = createPizza(type);
        pizza.prepare(); // other methods follow
        return pizza;
    }
    
    //customer returns a pizza
    public void ReturnPizza(Pizza pie) {
     if(!pie.HasAllSlices()){
       throw new CannotReturnPartiallyEatenPizzaException();
      }
     //hmm. maybe we can re-use this later on...
     bin.store(pie);        
    }
  
    //check if we have a pizza in the bin.
    protected Pizza HeresOneWeMadeEarlier(String type){
        if(bin.contains(type)){
          //we never said this was a *nice* pizza store!
            return bin.takeOutAndDustOff(type);
        }
        else{
          return null;
        }
    }

    protected abstract class Pizza createPizza(String type);

}

Maybe not a very nice place to get a Pizza, but I hope you see what I mean :)

By "acting on" I mean pizza.cut() etc, and by "querying" I mean pizza.isSpicy().

Bear in mind that these are methods on a Pizza , not a PizzaStore , and so therefore they don't really come into play here - when the caller is given their Pizza , they will be able to call whatever public methods a Pizza has ( eat , cut , share , etc.) and the PizzaStore probably doesn't have any interest in it any more.

Hope that helps.

Update

Just to address one point you had as well:

What if a 3rd-party class needed to know that the pizza is spicy before ordering?

In that case, you'd design a method on PizzaStore that would give a list of ingredients or options, and whether or not they were spicy.

  • Then when the client ordered a pizza (specifying a list of options), they already know if it's spicy based on the list of options they had.
  • Another client, if they had the order number, would presumably be able to look up the details of the order, which includes the options chosen at the time the pizza was requested, and thus can also determine if the pizza is spicy or not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM