|
| 1 | +--- |
| 2 | +title: Design Patterns |
| 3 | +created: 2025-04-10 |
| 4 | +tags: |
| 5 | + - creational |
| 6 | +--- |
| 7 | +## Definition |
| 8 | + |
| 9 | +The **Factory Method Pattern** defines an interface for creating an object but allows subclasses to decide which class to instantiate. In other words, it lets a class defer instantiation to its subclasses. This pattern helps in promoting loose coupling, encapsulating object creation, and enabling easier scalability when new types are added. |
| 10 | + |
| 11 | +--- |
| 12 | +## Real-World Analogy |
| 13 | + |
| 14 | +Imagine you have a **PizzaStore**. Popular pizza chains like Domino's or Pizza Hut have standardized processes so that anyone can order a pizza without knowing the details of its preparation. In our analogy, the pizza creation process is abstracted so that different stores can offer their own varieties of pizza. |
| 15 | + |
| 16 | +Initially, you might have a base `Pizza` class with properties like name, dough, and sauce, and standard methods for preparing, baking, cutting, and boxing the pizza. Various concrete pizza classes (such as `NyStyleCheesePizza` and `ChicagoStylePaneerPizza`) extend this base class. The `PizzaStore` uses these classes to fulfill customer orders. |
| 17 | + |
| 18 | +However, as your pizza business expands, you may introduce new pizza types and different styles across various stores. Rather than updating one monolithic method to handle all these variations, the Factory Method Pattern enables each store (or subclass) to decide which pizza types to create. This approach decouples the client code (the order processing) from the creation of the concrete pizza objects. |
| 19 | + |
| 20 | +--- |
| 21 | +## Initial Implementation |
| 22 | + |
| 23 | +Below is an example of the initial design using a single `PizzaStore` class that handles different pizza types through conditional statements in the `createPizza` method: |
| 24 | +```java title="Pizza.java" |
| 25 | +abstract class Pizza { |
| 26 | + public String name; |
| 27 | + public String dough; |
| 28 | + public String sauce; |
| 29 | + |
| 30 | + // Prepares the pizza by printing the preparation message |
| 31 | + public void prepare() { |
| 32 | + System.out.println(this.name + " is being prepared."); |
| 33 | + } |
| 34 | + |
| 35 | + // Bakes the pizza by printing the baking message |
| 36 | + public void bake() { |
| 37 | + System.out.println("Pizza is baking."); |
| 38 | + } |
| 39 | + |
| 40 | + // Cuts the pizza by printing the cutting message |
| 41 | + public void cut() { |
| 42 | + System.out.println("Pizza has been cut."); |
| 43 | + } |
| 44 | + |
| 45 | + // Boxes the pizza by printing the boxing message |
| 46 | + public void box() { |
| 47 | + System.out.println("Pizza is boxed and ready for delivery."); |
| 48 | + } |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +For example, you might create specialized pizza classes like these: |
| 53 | +```java title="NyStyleCheesePizza.java" |
| 54 | +class NyStyleCheesePizza extends Pizza { |
| 55 | + public NyStyleCheesePizza() { |
| 56 | + this.name = "NY Style Cheese Pizza"; |
| 57 | + this.dough = "NY Style Dough"; |
| 58 | + this.sauce = "NY Style Sauce"; |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +```java title="ChicagoStylePaneerPizza.java" |
| 64 | +class ChicagoStylePaneerPizza extends Pizza { |
| 65 | + public ChicagoStylePaneerPizza() { |
| 66 | + this.name = "Chicago Paneer Pizza"; |
| 67 | + this.dough = "Chicago Special Paneer Dough"; |
| 68 | + this.sauce = "Chicago Homemade Sauce"; |
| 69 | + } |
| 70 | +} |
| 71 | +``` |
| 72 | +And the initial `PizzaStore` might be implemented as: |
| 73 | +```java title="PizzaStore.java" |
| 74 | +class PizzaStore { |
| 75 | + |
| 76 | + // Processes the order by creating the pizza and performing standard steps |
| 77 | + public void orderPizza(String type) { |
| 78 | + Pizza pizza = createPizza(type); |
| 79 | + if (pizza != null) { |
| 80 | + pizza.prepare(); |
| 81 | + pizza.bake(); |
| 82 | + pizza.cut(); |
| 83 | + pizza.box(); |
| 84 | + } else { |
| 85 | + System.out.println("Sorry, we do not have that type of pizza."); |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + // Selects the Pizza based on the type provided |
| 90 | + public Pizza createPizza(String type) { |
| 91 | + if (type.equalsIgnoreCase("cheese")) { |
| 92 | + return new NyStyleCheesePizza(); |
| 93 | + } else if (type.equalsIgnoreCase("paneer")) { |
| 94 | + return new ChicagoStylePaneerPizza(); |
| 95 | + } |
| 96 | + return null; |
| 97 | + } |
| 98 | +} |
| 99 | +``` |
| 100 | +**Problem:** |
| 101 | +This approach forces you to modify the `createPizza` method whenever you add new pizza types. It also makes it difficult to accommodate differing pizza styles across various stores, since the pizza creation logic is centralized rather than distributed among specialized stores. |
| 102 | + |
| 103 | +--- |
| 104 | +## Applying the Factory Method Pattern |
| 105 | + |
| 106 | +To overcome these issues, we modify `PizzaStore` into an **abstract class**, deferring the decision of which pizza to create to its subclasses. This enables each store to have its own implementation of the `createPizza` method and supply its unique pizza offerings. |
| 107 | +### Revised Design |
| 108 | +The design diagram for the Factory Method pattern can be outlined as follows: |
| 109 | + |
| 110 | +```mermaid |
| 111 | +classDiagram |
| 112 | + %% Base Pizza Class |
| 113 | + class Pizza { |
| 114 | + <<abstract>> |
| 115 | + +String name |
| 116 | + +String dough |
| 117 | + +String sauce |
| 118 | + +prepare() |
| 119 | + +bake() |
| 120 | + +cut() |
| 121 | + +box() |
| 122 | + } |
| 123 | + |
| 124 | + %% New York Style Pizzas |
| 125 | + class NyStyleCheesePizza { |
| 126 | + +NyStyleCheesePizza() |
| 127 | + } |
| 128 | + class NyStyleMayoPizza { |
| 129 | + +NyStyleMayoPizza() |
| 130 | + } |
| 131 | +
|
| 132 | + %% Chicago Style Pizzas |
| 133 | + class ChicagostyleSchezwanPizza { |
| 134 | + +ChicagostyleSchezwanPizza() |
| 135 | + } |
| 136 | + class ChicagoStylePaneerPizza { |
| 137 | + +ChicagoStylePaneerPizza() |
| 138 | + } |
| 139 | + Pizza <|-- NyStyleCheesePizza |
| 140 | + Pizza <|-- NyStyleMayoPizza |
| 141 | + Pizza <|-- ChicagostyleSchezwanPizza |
| 142 | + Pizza <|-- ChicagoStylePaneerPizza |
| 143 | +``` |
| 144 | + |
| 145 | +```mermaid |
| 146 | +classDiagram |
| 147 | + %% Abstract Pizza Store |
| 148 | + class PizzaStore { |
| 149 | + <<abstract>> |
| 150 | + +orderPizza(String type) |
| 151 | + +createPizza(String type) |
| 152 | + } |
| 153 | + |
| 154 | + class NyPizzaStore { |
| 155 | + +createPizza(String type) |
| 156 | + } |
| 157 | + |
| 158 | + class ChicagoPizzaStore { |
| 159 | + +createPizza(String type) |
| 160 | + } |
| 161 | + |
| 162 | + PizzaStore <|-- NyPizzaStore |
| 163 | + PizzaStore <|-- ChicagoPizzaStore |
| 164 | +``` |
| 165 | +### Implementing the Revised Pattern |
| 166 | + |
| 167 | +1. **Abstract PizzaStore Class:** |
| 168 | + The abstract class `PizzaStore` defines the common process (`orderPizza`) and declares the abstract `createPizza` method. Each subclass will implement the creation logic specific to its style. |
| 169 | + ```java title="PizzaStore.java" |
| 170 | + abstract class PizzaStore { |
| 171 | + |
| 172 | + // Processes the order by creating the pizza and performing the standard steps |
| 173 | + public void orderPizza(String type) { |
| 174 | + Pizza pizza = createPizza(type); |
| 175 | + if (pizza != null) { |
| 176 | + pizza.prepare(); |
| 177 | + pizza.bake(); |
| 178 | + pizza.cut(); |
| 179 | + pizza.box(); |
| 180 | + } else { |
| 181 | + System.out.println("Sorry, we do not have that type of pizza."); |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + // Abstract method for creating a pizza of a given type |
| 186 | + abstract public Pizza createPizza(String type); |
| 187 | + } |
| 188 | + ``` |
| 189 | + |
| 190 | +2. **New York Pizza Store:** |
| 191 | + The `NyPizzaStore` subclass implements `createPizza` by supplying New York style pizzas. |
| 192 | + ```java title="NyPizzaStore.java" |
| 193 | + // Pizza store for New York style pizzas. |
| 194 | + class NyPizzaStore extends PizzaStore { |
| 195 | + @Override |
| 196 | + public Pizza createPizza(String type) { |
| 197 | + if (type.equalsIgnoreCase("cheese")) { |
| 198 | + return new NyStyleCheesePizza(); |
| 199 | + } else if (type.equalsIgnoreCase("mayonnaise")) { |
| 200 | + return new NyStyleMayoPizza(); |
| 201 | + } |
| 202 | + return null; |
| 203 | + } |
| 204 | + } |
| 205 | + ``` |
| 206 | + |
| 207 | +3. **Chicago Pizza Store:** |
| 208 | + The `ChicagoPizzaStore` subclass implements `createPizza` for Chicago style pizzas. |
| 209 | + ```java title="ChicagoPizzaStore.java" |
| 210 | + // Pizza store for Chicago style pizzas. |
| 211 | + class ChicagoPizzaStore extends PizzaStore { |
| 212 | + @Override |
| 213 | + public Pizza createPizza(String type) { |
| 214 | + if (type.equalsIgnoreCase("schezwan")) { |
| 215 | + return new ChicagostyleSchezwanPizza(); |
| 216 | + } else if (type.equalsIgnoreCase("paneer")) { |
| 217 | + return new ChicagoStylePaneerPizza(); |
| 218 | + } |
| 219 | + return null; |
| 220 | + } |
| 221 | + } |
| 222 | + ``` |
| 223 | + |
| 224 | +4. **Main Class to Demonstrate the Pattern:** |
| 225 | + The following code snippet shows how the client code interacts with these stores without knowing the details of pizza creation. |
| 226 | + ```java title="FactoryMethod.java" |
| 227 | + public class FactoryMethod { |
| 228 | + public static void main(String[] args) { |
| 229 | + // Ordering the cheese pizza from the New York Pizza Store |
| 230 | + PizzaStore nyPizzaStore = new NyPizzaStore(); |
| 231 | + nyPizzaStore.orderPizza("cheese"); |
| 232 | + |
| 233 | + // Ordering the paneer pizza from the Chicago Pizza Store |
| 234 | + PizzaStore chicagoStore = new ChicagoPizzaStore(); |
| 235 | + chicagoStore.orderPizza("paneer"); |
| 236 | + } |
| 237 | + } |
| 238 | + ``` |
| 239 | +**Output:** |
| 240 | +``` |
| 241 | +NY Style Cheese Pizza is being prepared. |
| 242 | +Pizza is baking. |
| 243 | +Pizza has been cut. |
| 244 | +Pizza is boxed and ready for delivery. |
| 245 | + |
| 246 | +Chicago Paneer Pizza is being prepared. |
| 247 | +Pizza is baking. |
| 248 | +Pizza has been cut. |
| 249 | +Pizza is boxed and ready for delivery. |
| 250 | +``` |
| 251 | +--- |
| 252 | +Next, we will discuss the Abstract Factory Pattern. |
0 commit comments