Skip to content

Commit b8dfb31

Browse files
koriymclaude
andcommitted
improve: refine pattern documentation and add tutorial structure
- Simplify code comments in decorator-aop.md - Add OCP abbreviation in strategy-pattern.md - Enhance stateless strategy explanation with thread-safety details - Improve section structure in null-object-pattern.md with unified emphasis - Update method comments to /** */ format in repository-pattern.md - Add new tutorial files for both English and Japanese versions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent b5d6ca7 commit b8dfb31

File tree

26 files changed

+8079
-12
lines changed

26 files changed

+8079
-12
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
---
2+
layout: docs-en
3+
title: Factory Pattern - Object Creation Patterns
4+
category: Manual
5+
permalink: /manuals/1.0/en/tutorial/02-object-creation-patterns/factory-pattern.html
6+
---
7+
8+
# Factory Pattern - Object Creation Patterns
9+
10+
## Learning Objectives
11+
12+
- Understand challenges of object creation requiring runtime parameters
13+
- Learn how the Factory Pattern complements DI's limitations
14+
- Decide when to use Factory vs Provider patterns
15+
16+
## The Problem: Mixing Runtime Parameters with Configuration-Time Dependencies
17+
18+
DI containers build the dependency graph at configuration time. But what if you need parameters that are only known at runtime?
19+
20+
```php
21+
class OrderProcessor
22+
{
23+
public function __construct(
24+
private int $orderId, // ← Runtime parameter (user input)
25+
private PaymentServiceInterface $paymentService, // ← Configuration-time dependency (DI)
26+
private InventoryServiceInterface $inventoryService
27+
) {}
28+
}
29+
30+
// ❌ Problem: DI container doesn't know orderId
31+
$processor = $injector->getInstance(OrderProcessor::class); // How to pass orderId?
32+
```
33+
34+
### Why This Is a Problem
35+
36+
1. **Different Nature of Dependencies**
37+
- `PaymentService`: Determined at application startup (configuration)
38+
- `orderId`: Determined by user request (runtime)
39+
40+
2. **DI's Limitation**
41+
- DI container knows "what to inject"
42+
- But "which value to inject" is unknown until runtime
43+
44+
3. **Temptation for Anti-patterns**
45+
```php
46+
// Pass via setter? → Mutable state, unclear dependencies
47+
$processor->setOrderId($orderId);
48+
49+
// Service Locator? → Loses DI benefits
50+
$processor = new OrderProcessor($orderId, $container->get(...), ...);
51+
```
52+
53+
## Solution: Factory Pattern
54+
55+
**Factory's Role**: Bridge between runtime parameters and configuration-time dependencies
56+
57+
```php
58+
// 1. Factory Interface
59+
interface OrderProcessorFactoryInterface
60+
{
61+
public function create(int $orderId): OrderProcessor;
62+
}
63+
64+
// 2. Factory Implementation (injects configuration-time dependencies)
65+
class OrderProcessorFactory implements OrderProcessorFactoryInterface
66+
{
67+
public function __construct(
68+
private PaymentServiceInterface $paymentService,
69+
private InventoryServiceInterface $inventoryService
70+
) {}
71+
72+
public function create(int $orderId): OrderProcessor
73+
{
74+
return new OrderProcessor(
75+
$orderId, // Runtime parameter
76+
$this->paymentService, // Configuration-time dependency
77+
$this->inventoryService
78+
);
79+
}
80+
}
81+
82+
// 3. Usage
83+
class OrderController
84+
{
85+
public function __construct(
86+
private OrderProcessorFactoryInterface $factory // Inject factory
87+
) {}
88+
89+
public function processOrder(Request $request): void
90+
{
91+
$orderId = $request->get('order_id');
92+
$processor = $this->factory->create($orderId); // ✅ Create at runtime
93+
$processor->process();
94+
}
95+
}
96+
```
97+
98+
## Pattern Essence
99+
100+
```
101+
Runtime Parameter Flow:
102+
Request → Controller → Factory.create(param) → New Object
103+
104+
Configuration-Time Dependency Flow:
105+
DI Container → Factory.__construct(deps) → Factory.create() → New Object
106+
```
107+
108+
### What Factory Solves
109+
110+
1. **Separation of Responsibilities**
111+
- Controller: Obtains runtime parameters
112+
- Factory: Object creation
113+
- DI Container: Resolves configuration-time dependencies
114+
115+
2. **Testability**
116+
- Factory can be swapped with test implementation
117+
- Test without starting actual services
118+
119+
3. **Clear Dependencies**
120+
- All dependencies received via constructor
121+
- Immutable after construction
122+
123+
## Decision Criteria
124+
125+
### When to Use Factory Pattern
126+
127+
| Situation | Reason |
128+
|-----------|--------|
129+
| **Runtime parameters needed** | User input, request data |
130+
| **Multiple instances of same type** | Different parameters in loops |
131+
| **Conditional creation** | Different types based on runtime conditions |
132+
133+
### When to Consider Other Patterns
134+
135+
| Situation | Alternative Pattern |
136+
|-----------|-------------------|
137+
| **No runtime parameters** | Direct DI |
138+
| **Complex initialization only** | Provider binding |
139+
| **Singleton** | Scope configuration |
140+
141+
### Decision Flow
142+
143+
```
144+
Object creation needed
145+
146+
├─ Runtime parameters required?
147+
│ ├─ YES → ✅ Factory Pattern
148+
│ └─ NO → Next question
149+
150+
├─ Complex initialization?
151+
│ ├─ YES → Provider Pattern (next section)
152+
│ └─ NO → Regular DI binding is sufficient
153+
```
154+
155+
## Common Anti-patterns
156+
157+
### God Factory
158+
159+
```php
160+
// ❌ Generic factory that creates anything
161+
interface GenericFactoryInterface
162+
{
163+
public function create(string $class, array $params): object;
164+
}
165+
166+
// ✅ Type-safe dedicated factory
167+
interface OrderProcessorFactoryInterface
168+
{
169+
public function create(int $orderId): OrderProcessor;
170+
}
171+
```
172+
173+
**Why it's a problem**: Loss of type safety, unclear interface contract
174+
175+
### Business Logic in Factory
176+
177+
```php
178+
// ❌ Factory has business logic
179+
public function create(int $orderId): OrderProcessor
180+
{
181+
$order = $this->repository->find($orderId);
182+
if ($order->getTotal() > 10000) { // Business rule
183+
$this->notify($order);
184+
}
185+
return new OrderProcessor(...);
186+
}
187+
188+
// ✅ Factory only creates
189+
public function create(int $orderId): OrderProcessor
190+
{
191+
return new OrderProcessor($orderId, $this->service, ...);
192+
}
193+
```
194+
195+
**Why it's a problem**: Single Responsibility Principle violation, unclear factory responsibility
196+
197+
## Relationship with SOLID Principles
198+
199+
- **SRP**: Factory is responsible only for "object creation"
200+
- **OCP**: Adding new types doesn't modify existing code
201+
- **DIP**: Depends on interfaces, eliminates dependency on concrete classes
202+
203+
## Summary
204+
205+
### Factory Pattern Core
206+
207+
- **Complements DI container's limitations**: Handles runtime parameters
208+
- **Clear responsibilities**: Separates creation logic
209+
- **Testability**: Factory can be swapped
210+
211+
### Next Steps
212+
213+
If your challenge is **complex initialization logic** rather than runtime parameters, the Provider Pattern is appropriate.
214+
215+
**Continue to:** [Provider Pattern](provider-pattern.html)
216+
217+
---
218+
219+
The Factory Pattern clarifies the boundary between **configuration-time** and **runtime**. Understanding this is key to mastering DI.

0 commit comments

Comments
 (0)