Design Patterns: Factory

Introduction

The Factory pattern is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. It abstracts the instantiation process, letting the client code rely on a common interface rather than concrete implementations, which promotes flexibility and maintainability.

Purpose

Creates objects without specifying the exact class of the object that will be created.

Use Cases

Object creation where subclasses decide which class to instantiate.

1from abc import ABC, abstractmethod
2
3# Abstract Product
4class Shape(ABC):
5 @abstractmethod
6 def draw(self):
7 pass
8
9# Concrete Products
10class Circle(Shape):
11 def draw(self):
12 return "Drawing a Circle"
13
14class Square(Shape):
15 def draw(self):
16 return "Drawing a Square"
17
18# Factory
19class ShapeFactory:
20 @staticmethod
21 def get_shape(shape_type):
22 if shape_type == "circle":
23 return Circle()
24 elif shape_type == "square":
25 return Square()
26 else:
27 raise ValueError("Unknown shape type")
28
29# Usage
30factory = ShapeFactory()
31shape1 = factory.get_shape("circle")
32shape2 = factory.get_shape("square")
33print(shape1.draw()) # Drawing a Circle
34print(shape2.draw()) # Drawing a Square
1// Abstract Product is not enforced in JS, but we define concrete products
2class Circle {
3 draw() {
4 return "Drawing a Circle";
5 }
6}
7
8class Square {
9 draw() {
10 return "Drawing a Square";
11 }
12}
13
14// Factory
15class ShapeFactory {
16 static getShape(shapeType) {
17 if (shapeType === "circle") {
18 return new Circle();
19 } else if (shapeType === "square") {
20 return new Square();
21 } else {
22 throw new Error("Unknown shape type");
23 }
24 }
25}
26
27// Usage
28const shape1 = ShapeFactory.getShape("circle");
29const shape2 = ShapeFactory.getShape("square");
30console.log(shape1.draw()); // Drawing a Circle
31console.log(shape2.draw()); // Drawing a Square
1// Abstract Product
2abstract class Shape {
3 String draw();
4}
5
6// Concrete Products
7class Circle implements Shape {
8
9 String draw() => "Drawing a Circle";
10}
11
12class Square implements Shape {
13
14 String draw() => "Drawing a Square";
15}
16
17// Factory
18class ShapeFactory {
19 static Shape getShape(String shapeType) {
20 if (shapeType == "circle") {
21 return Circle();
22 } else if (shapeType == "square") {
23 return Square();
24 } else {
25 throw ArgumentError("Unknown shape type");
26 }
27 }
28}
29
30// Usage
31void main() {
32 var shape1 = ShapeFactory.getShape("circle");
33 var shape2 = ShapeFactory.getShape("square");
34 print(shape1.draw()); // Drawing a Circle
35 print(shape2.draw()); // Drawing a Square
36}
1# Abstract Product is not enforced in Ruby, but we define concrete products
2class Circle
3 def draw
4 "Drawing a Circle"
5 end
6end
7
8class Square
9 def draw
10 "Drawing a Square"
11 end
12end
13
14# Factory
15class ShapeFactory
16 def self.get_shape(shape_type)
17 case shape_type
18 when "circle"
19 Circle.new
20 when "square"
21 Square.new
22 else
23 raise "Unknown shape type"
24 end
25 end
26end
27
28# Usage
29shape1 = ShapeFactory.get_shape("circle")
30shape2 = ShapeFactory.get_shape("square")
31puts shape1.draw # Drawing a Circle
32puts shape2.draw # Drawing a Square

Conclusion

The Factory pattern is useful when your code needs to create objects dynamically without hard-coding their concrete classes. It simplifies object creation, encourages loose coupling, and makes it easier to introduce new types of objects or modify creation logic without affecting the client code. Common use cases include creating different shapes, database connections, or user interface components based on configuration or runtime conditions.