Skip to content

Commit 2f02d6e

Browse files
New Pattern Abstract Factory, Removed the ending Breakline from each doc
1 parent a898285 commit 2f02d6e

6 files changed

+401
-10
lines changed

content/Abstract Factory Pattern.md

+394
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
1+
---
2+
title: Abstract Factory Pattern
3+
created: 2025-04-22
4+
tags:
5+
- creational
6+
---
7+
## Definition
8+
9+
The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete class.
10+
11+
---
12+
## Real World Analogy
13+
14+
In the previous example of the [[Factory Method Pattern]], we demonstrated ordering pizzas through a `PizzaStore`. However, each store may use different local ingredients, dough, sauce, and cheese, so a pizza with the same name can taste different in different regions. For instance, a New York style pizza uses thin crust dough and marinara sauce, while a Chicago style pizza uses thick crust dough and plum tomato sauce.
15+
16+
Using the Factory Method Pattern, we would need to create separate pizza classes for each store, even though the pizza types (like `CheesePizza` or `SchezwanPizza`) are conceptually the same. The only difference between them is the regional ingredients.
17+
18+
With the Abstract Factory Pattern, we introduce an abstract ingredient factory that the `PizzaStore` uses to create dough, sauce, and cheese. Each store provides its own concrete factory implementation. When ordering a pizza, the store’s factory applies the correct regional ingredients automatically. If a new store opens, you simply add its ingredient factory without modifying the pizza ordering logic.
19+
20+
```java title="Dough.java"
21+
// Interface for creating Dough
22+
interface Dough {
23+
public String getName();
24+
}
25+
```
26+
27+
```java title="Sauce.java"
28+
// Interface for creating Sauce based on regional style
29+
interface Sauce {
30+
public String getSauce();
31+
}
32+
```
33+
34+
```java title="Cheese.java"
35+
// Interface for creating Cheese based on regional style
36+
interface Cheese {
37+
public String getCheese();
38+
}
39+
```
40+
41+
These interfaces define the contracts for dough, sauce, and cheese. Below we implement them for New York style and Chicago style ingredients.
42+
43+
```java title="NYPizzaIngredients.java"
44+
// Creating Dough, Sauce, and Cheese based on New York style
45+
class ThinCrustDough implements Dough {
46+
@Override
47+
public String getName() {
48+
return "ThinCrustDough - NewYorkStore";
49+
}
50+
}
51+
52+
class ReggianoCheese implements Cheese {
53+
@Override
54+
public String getCheese() {
55+
return "ReggianoCheese - NewYorkStore";
56+
}
57+
}
58+
59+
class MarinaraSauce implements Sauce {
60+
@Override
61+
public String getSauce() {
62+
return "MarinaraSauce - NewYorkStore";
63+
}
64+
}
65+
```
66+
67+
```java title="ChicagoPizzaIngredient.java"
68+
// Creating Dough, Sauce, and Cheese for the Chicago Pizza Store
69+
class MozzarellaCheese implements Cheese {
70+
@Override
71+
public String getCheese() {
72+
return "MozzarellaCheese - ChicagoStore";
73+
}
74+
}
75+
76+
class ThickCrustDough implements Dough {
77+
@Override
78+
public String getName() {
79+
return "ThickCrustDough - ChicagoStore";
80+
}
81+
}
82+
83+
class PlumTomatoSauce implements Sauce {
84+
@Override
85+
public String getSauce() {
86+
return "PlumTomatoSauce - ChicagoStore";
87+
}
88+
}
89+
```
90+
91+
Here we implement New York style and Chicago style ingredients by implementing the interfaces. You could also group all regional implementations in a single file if you prefer.
92+
93+
```java title="PizzaIngredientFactory.java"
94+
// Creating the interface for PizzaIngredientFactory
95+
interface PizzaIngredientFactory {
96+
public Dough createDough();
97+
public Sauce createSauce();
98+
public Cheese createCheese();
99+
}
100+
```
101+
102+
We now create the `PizzaIngredientFactory` abstraction, which defines methods to produce each ingredient. Next we implement concrete factories for New York and Chicago styles.
103+
104+
```java title="NyPizzaIngredientFactory.java"
105+
// PizzaIngredientFactory implementation for NYPizzaStore
106+
class NYPizzaIngredientFactory implements PizzaIngredientFactory {
107+
@Override
108+
public Dough createDough() {
109+
return new ThinCrustDough();
110+
}
111+
112+
@Override
113+
public Sauce createSauce() {
114+
return new MarinaraSauce();
115+
}
116+
117+
@Override
118+
public Cheese createCheese() {
119+
return new ReggianoCheese();
120+
}
121+
}
122+
```
123+
124+
```java title="ChicagoPizzaIngredientFactory.java"
125+
// PizzaIngredientFactory implementation for ChicagoPizzaStore
126+
class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
127+
@Override
128+
public Dough createDough() {
129+
return new ThickCrustDough();
130+
}
131+
132+
@Override
133+
public Sauce createSauce() {
134+
return new PlumTomatoSauce();
135+
}
136+
137+
@Override
138+
public Cheese createCheese() {
139+
return new MozzarellaCheese();
140+
}
141+
}
142+
```
143+
144+
Next we update the `Pizza` abstract class so each pizza can prepare itself using the provided ingredient factory.
145+
146+
```java title="Pizza.java"
147+
// Base class for creating a Pizza
148+
abstract class Pizza {
149+
public String name;
150+
public Dough dough;
151+
public Sauce sauce;
152+
public Cheese cheese;
153+
154+
// Each pizza must prepare itself with regional ingredients
155+
abstract public void prepare();
156+
157+
// Common baking step
158+
public void bake() {
159+
System.out.println("Baking " + name);
160+
}
161+
162+
// Common cutting step
163+
public void cut() {
164+
System.out.println("Cutting " + name);
165+
}
166+
167+
// Common boxing step
168+
public void box() {
169+
System.out.println("Boxing " + name);
170+
}
171+
}
172+
```
173+
174+
The abstract `prepare` method enforces that each pizza subclass defines its own preparation steps. The `bake`, `cut`, and `box` methods provide shared behavior.
175+
176+
```java title="CheesePizza.java"
177+
// Cheese Pizza class
178+
class CheesePizza extends Pizza {
179+
// Reference to the ingredient factory
180+
private PizzaIngredientFactory ingredientFactory;
181+
182+
public CheesePizza(PizzaIngredientFactory factory) {
183+
this.ingredientFactory = factory;
184+
this.name = "Cheese Pizza";
185+
}
186+
187+
@Override
188+
public void prepare() {
189+
System.out.println("Preparing " + name);
190+
this.dough = ingredientFactory.createDough();
191+
this.sauce = ingredientFactory.createSauce();
192+
this.cheese = ingredientFactory.createCheese();
193+
}
194+
}
195+
```
196+
197+
```java title="SchezwanPizza.java"
198+
// Schezwan Pizza class
199+
class SchezwanPizza extends Pizza {
200+
// Reference to the ingredient factory
201+
private PizzaIngredientFactory ingredientFactory;
202+
203+
public SchezwanPizza(PizzaIngredientFactory factory) {
204+
this.ingredientFactory = factory;
205+
this.name = "Schezwan Pizza";
206+
}
207+
208+
@Override
209+
public void prepare() {
210+
System.out.println("Preparing " + name);
211+
this.dough = ingredientFactory.createDough();
212+
this.sauce = ingredientFactory.createSauce();
213+
this.cheese = ingredientFactory.createCheese();
214+
}
215+
}
216+
```
217+
218+
In each pizza class we store the `PizzaIngredientFactory` passed into the constructor. This factory determines which regional ingredients will be used during preparation.
219+
220+
Each `PizzaStore` subclass injects its own ingredient factory when creating pizzas:
221+
222+
```java title="NyPizzaStore.java"
223+
// New York Pizza Store using NY ingredient factory
224+
class NyPizzaStore extends PizzaStore {
225+
private PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
226+
227+
@Override
228+
protected PizzaBase createPizza(String type) {
229+
if (type.equalsIgnoreCase("cheese")) {
230+
return new CheesePizza(ingredientFactory);
231+
} else if (type.equalsIgnoreCase("schezwan")) {
232+
return new SchezwanPizza(ingredientFactory);
233+
}
234+
return null;
235+
}
236+
}
237+
```
238+
239+
```java title="ChicagoPizzaStore.java"
240+
// Chicago Pizza Store using Chicago ingredient factory
241+
class ChicagoPizzaStore extends PizzaStore {
242+
private PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
243+
244+
@Override
245+
protected PizzaBase createPizza(String type) {
246+
if (type.equalsIgnoreCase("cheese")) {
247+
return new CheesePizza(ingredientFactory);
248+
} else if (type.equalsIgnoreCase("schezwan")) {
249+
return new SchezwanPizza(ingredientFactory);
250+
}
251+
return null;
252+
}
253+
}
254+
```
255+
256+
Finally, in our client code we order pizzas from different stores to see the Abstract Factory in action:
257+
258+
```java title="Program.java"
259+
// Ordering pizza from the New York store
260+
PizzaStore1 nyStore = new NyPizzaStore1();
261+
nyStore.orderPizza("cheese");
262+
263+
// Ordering pizza from the Chicago store
264+
PizzaStore1 chicagoStore = new ChicagoPizzaStore1();
265+
chicagoStore.orderPizza("schezwan");
266+
```
267+
268+
**Sample Output:**
269+
270+
```
271+
Preparing Cheese Pizza
272+
Baking Cheese Pizza
273+
Cutting Cheese Pizza
274+
Boxing Cheese Pizza
275+
Preparing Schezwan Pizza
276+
Baking Schezwan Pizza
277+
Cutting Schezwan Pizza
278+
Boxing Schezwan Pizza
279+
```
280+
281+
## Design
282+
283+
```mermaid
284+
classDiagram
285+
class Dough {
286+
<<Interface>>
287+
+String getName()
288+
}
289+
class Sauce {
290+
<<Interface>>
291+
+String getSauce()
292+
}
293+
class Cheese {
294+
<<Interface>>
295+
+String getCheese()
296+
}
297+
class ThinCrustDough {
298+
+String getName()
299+
}
300+
class ThickCrustDough {
301+
+String getName()
302+
}
303+
class MarinaraSauce {
304+
+String getSauce()
305+
}
306+
class PlumTomatoSauce {
307+
+String getSauce()
308+
}
309+
class ReggianoCheese {
310+
+String getCheese()
311+
}
312+
class MozzarellaCheese {
313+
+String getCheese()
314+
}
315+
Dough <|.. ThinCrustDough
316+
Dough <|.. ThickCrustDough
317+
Sauce <|.. MarinaraSauce
318+
Sauce <|.. PlumTomatoSauce
319+
Cheese <|.. ReggianoCheese
320+
Cheese <|.. MozzarellaCheese
321+
```
322+
323+
```mermaid
324+
classDiagram
325+
class PizzaIngredientFactory {
326+
<<Interface>>
327+
+createDough() Dough
328+
+createSauce() Sauce
329+
+createCheese() Cheese
330+
}
331+
class NYPizzaIngredientFactory {
332+
+createDough() Dough
333+
+createSauce() Sauce
334+
+createCheese() Cheese
335+
}
336+
class ChicagoPizzaIngredientFactory {
337+
+createDough() Dough
338+
+createSauce() Sauce
339+
+createCheese() Cheese
340+
}
341+
PizzaIngredientFactory <|.. NYPizzaIngredientFactory
342+
PizzaIngredientFactory <|.. ChicagoPizzaIngredientFactory
343+
```
344+
345+
```mermaid
346+
classDiagram
347+
class PizzaBase {
348+
<<Abstract>>
349+
+ name: string
350+
+ dough: Dough
351+
+ sauce: Sauce
352+
+ cheese: Cheese
353+
+ prepare()
354+
+ bake()
355+
+ cut()
356+
+ box()
357+
}
358+
class CheesePizza {
359+
+CheesePizza(factory: PizzaIngredientFactory)
360+
+void prepare()
361+
}
362+
class SchezwanPizza {
363+
+SchezwanPizza(factory: PizzaIngredientFactory)
364+
+void prepare()
365+
}
366+
class PizzaStore {
367+
<<Abstract>>
368+
+orderPizza(type: String)
369+
#createPizza(type: String): PizzaBase
370+
}
371+
class NyPizzaStore {
372+
-PizzaIngredientFactory ingredientFactory
373+
}
374+
class ChicagoPizzaStore {
375+
-PizzaIngredientFactory ingredientFactory
376+
}
377+
PizzaBase <|-- CheesePizza
378+
PizzaBase <|-- SchezwanPizza
379+
PizzaStore <|-- NyPizzaStore
380+
PizzaStore <|-- ChicagoPizzaStore
381+
NyPizzaStore *-- PizzaIngredientFactory
382+
ChicagoPizzaStore *-- PizzaIngredientFactory
383+
PizzaStore ..> PizzaBase
384+
```
385+
386+
---
387+
## Design Principles
388+
389+
- **Encapsulate What Varies** - Identify the parts of the code that are going to change and encapsulate them into separate class just like the Strategy Pattern.
390+
- **Favor Composition Over Inheritance** - Instead of using inheritance on extending functionality, rather use composition by delegating behavior to other objects.
391+
- **Program to Interface not Implementations** - Write code that depends on Abstractions or Interfaces rather than Concrete Classes.
392+
- **Strive for Loosely coupled design between objects that interact** - When implementing a class, avoid tightly coupled classes. Instead, use loosely coupled objects by leveraging abstractions and interfaces. This approach ensures that the class does not heavily depend on other classes.
393+
- **Classes Should be Open for Extension But closed for Modification** - Design your classes so you can extend their behavior without altering their existing, stable code.
394+
- **Depend on Abstractions, Do not depend on concrete class** - Rely on interfaces or abstract types instead of concrete classes so you can swap implementations without altering client code.

content/Decorator Pattern.md

-1
Original file line numberDiff line numberDiff line change
@@ -320,4 +320,3 @@ DataInputStream dataStream = new DataInputStream(bufferedStream);
320320
- **Program to Interface not Implementations** - Write code that depends on Abstractions or Interfaces rather than Concrete Classes.
321321
- **Strive for Loosely coupled design between objects that interact** - When implementing a class, avoid tightly coupled classes. Instead, use loosely coupled objects by leveraging abstractions and interfaces. This approach ensures that the class does not heavily depend on other classes.
322322
- **Classes Should be Open for Extension But closed for Modification** - Design your classes so you can extend their behavior without altering their existing, stable code.
323-
----

0 commit comments

Comments
 (0)