Consider now that the Client changes her requirements further. (These clients are really clever bunch of people. They keep changing their requirements again and again and expect us engineers to meet the deadlines. Fortunately we have design patterns to come to our rescue). The client now wants to have specific glasses and straws for the drinks from the different fountains.
We are already aware of two previous design pattern suggestions:
"Encapsulate what varies"
"Encapsulate what varies"
"Depend on Abstractions, not on Concretions"
Its time to familiarize ourselves with yet another one:
"Favor Object composition over inheritance"
We will be using this set of suggestions to solve our problem.
Firstly we realize that we will need three methods inside our Factory now. Previously there was only createDrink() but now we need createGlass() and createStraw(). Both our Factories will have these methods so it would be a nice idea to abstract our Factories. As a result we will no longer keep our methods static in
Notice that the methods inside the Factory are actually Factory Methods.
Formally we may define the Abstract Factory as follows:
The Abstract Factory pattern is one level of abstraction higher than the ‘factory pattern’. You can use this pattern when you want to return one of several related classes of objects, each of which can return several different objects on request. In other words, the Abstract Factory is a factory object that returns one of several factories.
There are other mechanisms of using the AbstractFactory inside the Fountain class. I'll leave that discussion for a later time.
With all the advantages including abstraction and localization of information provided by the Abstract Factory pattern it is easy to get carried away. One should be careful in choosing this pattern over the Simple Factory or Factory Method pattern. For one extending the abstract factory is not easy because the AbstractFactory abstraction fixes the set of products that can be created. So adding new products, for example Toppings on Drinks, requires adding methods in the abstract factory which involves changing the AbstractFactory class and all its subclasses. Secondly, if the client needs to know of the subclasses, for example the actual drinks created, explicit down casting is required of drinks created from the concrete factories. Thirdly it is very difficult to add a new type of family of products in the abstract factory as the child class then needs to implement all the methods defined in the abstract class. For example if we wish to add a third type of fountain for RC-Cola and the RC-Cola does not have its own Straw it will be impossible to fit this RCColaFountainFactory as a child of the AbstractFountainFactory.
There are ways of getting around the third problem in two main ways. One is by making the AbstractFactory provide default implementations for some of the products (i.e. Drinks, Straws, Glasses). A second not so frequently used technique (and I think a very poor one) would be handling for null return values from the factory methods.