@aicgen/aicgen 1.0.0-beta.2 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/rules/api-design.md +649 -0
- package/.agent/rules/architecture.md +2507 -0
- package/.agent/rules/best-practices.md +622 -0
- package/.agent/rules/code-style.md +308 -0
- package/.agent/rules/design-patterns.md +577 -0
- package/.agent/rules/devops.md +230 -0
- package/.agent/rules/error-handling.md +417 -0
- package/.agent/rules/instructions.md +28 -0
- package/.agent/rules/language.md +786 -0
- package/.agent/rules/performance.md +710 -0
- package/.agent/rules/security.md +587 -0
- package/.agent/rules/testing.md +572 -0
- package/.agent/workflows/add-documentation.md +10 -0
- package/.agent/workflows/generate-integration-tests.md +10 -0
- package/.agent/workflows/generate-unit-tests.md +11 -0
- package/.agent/workflows/performance-audit.md +11 -0
- package/.agent/workflows/refactor-extract-module.md +12 -0
- package/.agent/workflows/security-audit.md +12 -0
- package/.gemini/instructions.md +4843 -0
- package/AGENTS.md +9 -11
- package/bun.lock +755 -4
- package/claude.md +2 -2
- package/config.example.yml +129 -0
- package/config.yml +38 -0
- package/data/guideline-mappings.yml +128 -0
- package/data/language/dart/async.md +289 -0
- package/data/language/dart/basics.md +280 -0
- package/data/language/dart/error-handling.md +355 -0
- package/data/language/dart/index.md +10 -0
- package/data/language/dart/testing.md +352 -0
- package/data/language/swift/basics.md +477 -0
- package/data/language/swift/concurrency.md +654 -0
- package/data/language/swift/error-handling.md +679 -0
- package/data/language/swift/swiftui-mvvm.md +795 -0
- package/data/language/swift/testing.md +708 -0
- package/data/version.json +10 -8
- package/dist/index.js +50295 -29101
- package/jest.config.js +46 -0
- package/package.json +13 -2
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
# Design Patterns Rules
|
|
2
|
+
|
|
3
|
+
# Base Patterns
|
|
4
|
+
|
|
5
|
+
## Value Object
|
|
6
|
+
|
|
7
|
+
Immutable object defined by its value, not identity.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
class Email {
|
|
11
|
+
private readonly value: string;
|
|
12
|
+
|
|
13
|
+
constructor(email: string) {
|
|
14
|
+
if (!this.isValid(email)) throw new Error('Invalid email');
|
|
15
|
+
this.value = email.toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
equals(other: Email): boolean {
|
|
19
|
+
return this.value === other.value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class Money {
|
|
24
|
+
constructor(
|
|
25
|
+
public readonly amount: number,
|
|
26
|
+
public readonly currency: Currency
|
|
27
|
+
) {
|
|
28
|
+
Object.freeze(this);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
add(other: Money): Money {
|
|
32
|
+
this.assertSameCurrency(other);
|
|
33
|
+
return new Money(this.amount + other.amount, this.currency);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Special Case (Null Object)
|
|
39
|
+
|
|
40
|
+
Replace null checks with polymorphism.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
abstract class Customer {
|
|
44
|
+
abstract getDiscount(): number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class RealCustomer extends Customer {
|
|
48
|
+
getDiscount(): number { return 0.1; }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class GuestCustomer extends Customer {
|
|
52
|
+
getDiscount(): number { return 0; } // No discount
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// No null checks needed
|
|
56
|
+
const customer = repo.findById(id) || new GuestCustomer();
|
|
57
|
+
const discount = customer.getDiscount();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Registry
|
|
61
|
+
|
|
62
|
+
Global access point for services.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
class ServiceRegistry {
|
|
66
|
+
private static services = new Map<string, any>();
|
|
67
|
+
|
|
68
|
+
static register<T>(key: string, service: T): void {
|
|
69
|
+
this.services.set(key, service);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static get<T>(key: string): T {
|
|
73
|
+
return this.services.get(key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Prefer dependency injection over registry
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Plugin
|
|
81
|
+
|
|
82
|
+
Extend behavior without modifying core code.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
interface ValidationPlugin {
|
|
86
|
+
validate(user: User): ValidationResult;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
class UserValidator {
|
|
90
|
+
private plugins: ValidationPlugin[] = [];
|
|
91
|
+
|
|
92
|
+
registerPlugin(plugin: ValidationPlugin): void {
|
|
93
|
+
this.plugins.push(plugin);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
validate(user: User): ValidationResult[] {
|
|
97
|
+
return this.plugins.map(p => p.validate(user));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Best Practices
|
|
103
|
+
|
|
104
|
+
- Use Value Objects to avoid primitive obsession
|
|
105
|
+
- Make Value Objects immutable
|
|
106
|
+
- Use Special Case instead of null checks
|
|
107
|
+
- Prefer dependency injection over Registry
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
# Concurrency Patterns
|
|
113
|
+
|
|
114
|
+
## Optimistic Locking
|
|
115
|
+
|
|
116
|
+
Detect conflicts on commit using version numbers.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
class Product {
|
|
120
|
+
constructor(
|
|
121
|
+
public id: string,
|
|
122
|
+
public name: string,
|
|
123
|
+
public version: number = 1
|
|
124
|
+
) {}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
class ProductRepository {
|
|
128
|
+
async save(product: Product): Promise<void> {
|
|
129
|
+
const result = await this.db.execute(
|
|
130
|
+
`UPDATE products SET name = $1, version = version + 1
|
|
131
|
+
WHERE id = $2 AND version = $3`,
|
|
132
|
+
[product.name, product.id, product.version]
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (result.rowCount === 0) {
|
|
136
|
+
throw new OptimisticLockException('Product was modified');
|
|
137
|
+
}
|
|
138
|
+
product.version++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Pessimistic Locking
|
|
144
|
+
|
|
145
|
+
Lock resources before editing.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
class LockManager {
|
|
149
|
+
async acquireLock(resourceId: string, ownerId: string): Promise<boolean> {
|
|
150
|
+
const existing = await this.db.queryOne(
|
|
151
|
+
'SELECT * FROM locks WHERE resource_id = $1 AND expires_at > NOW()',
|
|
152
|
+
[resourceId]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (existing) return false;
|
|
156
|
+
|
|
157
|
+
await this.db.execute(
|
|
158
|
+
'INSERT INTO locks (resource_id, owner_id, expires_at) VALUES ($1, $2, $3)',
|
|
159
|
+
[resourceId, ownerId, new Date(Date.now() + 30000)]
|
|
160
|
+
);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async releaseLock(resourceId: string, ownerId: string): Promise<void> {
|
|
165
|
+
await this.db.execute(
|
|
166
|
+
'DELETE FROM locks WHERE resource_id = $1 AND owner_id = $2',
|
|
167
|
+
[resourceId, ownerId]
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Coarse-Grained Lock
|
|
174
|
+
|
|
175
|
+
Lock entire aggregate rather than individual entities.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
class OrderRepository {
|
|
179
|
+
async save(order: Order): Promise<void> {
|
|
180
|
+
// Lock aggregate root, all children implicitly locked
|
|
181
|
+
await this.db.execute(
|
|
182
|
+
'SELECT * FROM orders WHERE id = $1 FOR UPDATE',
|
|
183
|
+
[order.id]
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Update order and all items in single transaction
|
|
187
|
+
await this.updateOrder(order);
|
|
188
|
+
await this.updateOrderItems(order.items);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Best Practices
|
|
194
|
+
|
|
195
|
+
- Use optimistic locking for low-contention scenarios
|
|
196
|
+
- Use pessimistic locking for high-contention or critical data
|
|
197
|
+
- Always set lock timeouts
|
|
198
|
+
- Implement retry logic with exponential backoff
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
# Data Access Patterns
|
|
204
|
+
|
|
205
|
+
## Repository
|
|
206
|
+
|
|
207
|
+
Collection-like interface for domain objects.
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
interface UserRepository {
|
|
211
|
+
findById(id: string): Promise<User | null>;
|
|
212
|
+
findByEmail(email: string): Promise<User | null>;
|
|
213
|
+
save(user: User): Promise<void>;
|
|
214
|
+
delete(user: User): Promise<void>;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
class PostgreSQLUserRepository implements UserRepository {
|
|
218
|
+
async findById(id: string): Promise<User | null> {
|
|
219
|
+
const row = await this.db.queryOne('SELECT * FROM users WHERE id = $1', [id]);
|
|
220
|
+
return row ? this.mapToUser(row) : null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Data Mapper
|
|
226
|
+
|
|
227
|
+
Complete separation between domain and persistence.
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
class UserMapper {
|
|
231
|
+
toDomain(row: DbRow): User {
|
|
232
|
+
return new User(row.id, row.name, new Email(row.email));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
toDatabase(user: User): DbRow {
|
|
236
|
+
return { id: user.id, name: user.name, email: user.email.toString() };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Unit of Work
|
|
242
|
+
|
|
243
|
+
Track changes and commit together.
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
class UnitOfWork {
|
|
247
|
+
private newObjects = new Set<any>();
|
|
248
|
+
private dirtyObjects = new Set<any>();
|
|
249
|
+
|
|
250
|
+
registerNew(obj: any): void { this.newObjects.add(obj); }
|
|
251
|
+
registerDirty(obj: any): void { this.dirtyObjects.add(obj); }
|
|
252
|
+
|
|
253
|
+
async commit(): Promise<void> {
|
|
254
|
+
await this.db.beginTransaction();
|
|
255
|
+
try {
|
|
256
|
+
for (const obj of this.newObjects) await this.insert(obj);
|
|
257
|
+
for (const obj of this.dirtyObjects) await this.update(obj);
|
|
258
|
+
await this.db.commit();
|
|
259
|
+
} catch (e) {
|
|
260
|
+
await this.db.rollback();
|
|
261
|
+
throw e;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Identity Map
|
|
268
|
+
|
|
269
|
+
Ensure each object loaded only once per session.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
class IdentityMap {
|
|
273
|
+
private map = new Map<string, any>();
|
|
274
|
+
|
|
275
|
+
get(id: string): any | null { return this.map.get(id) || null; }
|
|
276
|
+
put(id: string, obj: any): void { this.map.set(id, obj); }
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Best Practices
|
|
281
|
+
|
|
282
|
+
- Return domain objects from repositories
|
|
283
|
+
- Use one repository per aggregate root
|
|
284
|
+
- Keep repositories focused on persistence
|
|
285
|
+
- Don't leak database details into domain
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
# Distribution Patterns
|
|
291
|
+
|
|
292
|
+
## Remote Facade
|
|
293
|
+
|
|
294
|
+
Coarse-grained interface to reduce network calls.
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
// Bad: Multiple network calls
|
|
298
|
+
const customer = await api.getCustomer(id);
|
|
299
|
+
const orders = await api.getOrders(id);
|
|
300
|
+
const addresses = await api.getAddresses(id);
|
|
301
|
+
|
|
302
|
+
// Good: Single call via facade
|
|
303
|
+
const details = await api.getCustomerDetails(id);
|
|
304
|
+
// Returns { customer, orders, addresses }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Data Transfer Object (DTO)
|
|
308
|
+
|
|
309
|
+
Bundle data for transfer across boundaries.
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
interface OrderDTO {
|
|
313
|
+
id: string;
|
|
314
|
+
customerId: string;
|
|
315
|
+
items: OrderItemDTO[];
|
|
316
|
+
total: number;
|
|
317
|
+
status: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
class OrderDTOMapper {
|
|
321
|
+
static toDTO(order: Order): OrderDTO {
|
|
322
|
+
return {
|
|
323
|
+
id: order.id,
|
|
324
|
+
customerId: order.customer.id,
|
|
325
|
+
items: order.items.map(i => this.itemToDTO(i)),
|
|
326
|
+
total: order.total.amount,
|
|
327
|
+
status: order.status.toString()
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Gateway
|
|
334
|
+
|
|
335
|
+
Abstract external system access.
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
interface PaymentGateway {
|
|
339
|
+
charge(amount: Money, method: PaymentMethod): Promise<PaymentResult>;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
class StripeGateway implements PaymentGateway {
|
|
343
|
+
async charge(amount: Money, method: PaymentMethod): Promise<PaymentResult> {
|
|
344
|
+
const result = await this.stripe.paymentIntents.create({
|
|
345
|
+
amount: amount.cents,
|
|
346
|
+
currency: amount.currency
|
|
347
|
+
});
|
|
348
|
+
return this.mapToResult(result);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Service Stub
|
|
354
|
+
|
|
355
|
+
Test double for external services.
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
class StubPaymentGateway implements PaymentGateway {
|
|
359
|
+
private shouldSucceed = true;
|
|
360
|
+
|
|
361
|
+
async charge(amount: Money): Promise<PaymentResult> {
|
|
362
|
+
if (!this.shouldSucceed) throw new PaymentDeclinedError();
|
|
363
|
+
return { success: true, transactionId: 'stub-123' };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
configureFail(): void { this.shouldSucceed = false; }
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Best Practices
|
|
371
|
+
|
|
372
|
+
- Design facades around client use cases
|
|
373
|
+
- Keep DTOs simple and serializable
|
|
374
|
+
- Isolate vendor code in gateways
|
|
375
|
+
- Use stubs for testing, not production
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
# Domain Logic Patterns
|
|
381
|
+
|
|
382
|
+
## Transaction Script
|
|
383
|
+
|
|
384
|
+
Procedural approach - one procedure per operation.
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
async function transferMoney(fromId: string, toId: string, amount: number) {
|
|
388
|
+
const db = await Database.connect();
|
|
389
|
+
await db.beginTransaction();
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
const from = await db.query('SELECT * FROM accounts WHERE id = $1', [fromId]);
|
|
393
|
+
if (from.balance < amount) throw new Error('Insufficient funds');
|
|
394
|
+
|
|
395
|
+
await db.execute('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [amount, fromId]);
|
|
396
|
+
await db.execute('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [amount, toId]);
|
|
397
|
+
await db.commit();
|
|
398
|
+
} catch (e) {
|
|
399
|
+
await db.rollback();
|
|
400
|
+
throw e;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Use for:** Simple apps, CRUD, reports.
|
|
406
|
+
|
|
407
|
+
## Domain Model
|
|
408
|
+
|
|
409
|
+
Rich objects with behavior.
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
class Account {
|
|
413
|
+
constructor(private balance: Money, private overdraftLimit: Money) {}
|
|
414
|
+
|
|
415
|
+
withdraw(amount: Money): void {
|
|
416
|
+
if (!this.canWithdraw(amount)) throw new InsufficientFundsError();
|
|
417
|
+
this.balance = this.balance.subtract(amount);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
transfer(amount: Money, recipient: Account): void {
|
|
421
|
+
this.withdraw(amount);
|
|
422
|
+
recipient.deposit(amount);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Use for:** Complex business rules, rich domains.
|
|
428
|
+
|
|
429
|
+
## Service Layer
|
|
430
|
+
|
|
431
|
+
Application boundary coordinating domain objects.
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
class AccountService {
|
|
435
|
+
constructor(
|
|
436
|
+
private accountRepo: AccountRepository,
|
|
437
|
+
private unitOfWork: UnitOfWork
|
|
438
|
+
) {}
|
|
439
|
+
|
|
440
|
+
async transfer(fromId: string, toId: string, amount: Money): Promise<void> {
|
|
441
|
+
const from = await this.accountRepo.findById(fromId);
|
|
442
|
+
const to = await this.accountRepo.findById(toId);
|
|
443
|
+
|
|
444
|
+
from.transfer(amount, to); // Domain logic
|
|
445
|
+
|
|
446
|
+
await this.accountRepo.save(from);
|
|
447
|
+
await this.accountRepo.save(to);
|
|
448
|
+
await this.unitOfWork.commit();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
**Use for:** API boundaries, multiple clients, transaction coordination.
|
|
454
|
+
|
|
455
|
+
## Best Practices
|
|
456
|
+
|
|
457
|
+
- Choose pattern based on complexity
|
|
458
|
+
- Service layer orchestrates, domain model contains logic
|
|
459
|
+
- Keep services thin, domain objects rich
|
|
460
|
+
- Combine Domain Model + Service Layer for complex apps
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
# Gang of Four Patterns
|
|
466
|
+
|
|
467
|
+
## Creational
|
|
468
|
+
|
|
469
|
+
### Factory Method
|
|
470
|
+
```typescript
|
|
471
|
+
interface Logger { log(msg: string): void; }
|
|
472
|
+
|
|
473
|
+
abstract class Application {
|
|
474
|
+
abstract createLogger(): Logger;
|
|
475
|
+
run(): void { this.createLogger().log('Started'); }
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
class DevApp extends Application {
|
|
479
|
+
createLogger(): Logger { return new ConsoleLogger(); }
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Builder
|
|
484
|
+
```typescript
|
|
485
|
+
const query = new QueryBuilder()
|
|
486
|
+
.from('users')
|
|
487
|
+
.select('id', 'name')
|
|
488
|
+
.where('active = true')
|
|
489
|
+
.limit(10)
|
|
490
|
+
.build();
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Structural
|
|
494
|
+
|
|
495
|
+
### Adapter
|
|
496
|
+
```typescript
|
|
497
|
+
class PaymentAdapter implements PaymentProcessor {
|
|
498
|
+
constructor(private legacy: OldPaymentSystem) {}
|
|
499
|
+
|
|
500
|
+
async process(amount: number): Promise<boolean> {
|
|
501
|
+
return this.legacy.makePayment(amount);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Decorator
|
|
507
|
+
```typescript
|
|
508
|
+
interface Coffee { cost(): number; }
|
|
509
|
+
|
|
510
|
+
class MilkDecorator implements Coffee {
|
|
511
|
+
constructor(private coffee: Coffee) {}
|
|
512
|
+
cost(): number { return this.coffee.cost() + 2; }
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
let coffee: Coffee = new SimpleCoffee();
|
|
516
|
+
coffee = new MilkDecorator(coffee);
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### Facade
|
|
520
|
+
```typescript
|
|
521
|
+
class ComputerFacade {
|
|
522
|
+
start(): void {
|
|
523
|
+
this.cpu.freeze();
|
|
524
|
+
this.memory.load(0, this.hdd.read(0, 1024));
|
|
525
|
+
this.cpu.execute();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## Behavioral
|
|
531
|
+
|
|
532
|
+
### Strategy
|
|
533
|
+
```typescript
|
|
534
|
+
interface SortStrategy { sort(data: number[]): number[]; }
|
|
535
|
+
|
|
536
|
+
class Sorter {
|
|
537
|
+
constructor(private strategy: SortStrategy) {}
|
|
538
|
+
sort(data: number[]): number[] { return this.strategy.sort(data); }
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Observer
|
|
543
|
+
```typescript
|
|
544
|
+
class Stock {
|
|
545
|
+
private observers: Observer[] = [];
|
|
546
|
+
|
|
547
|
+
attach(o: Observer): void { this.observers.push(o); }
|
|
548
|
+
notify(): void { this.observers.forEach(o => o.update(this)); }
|
|
549
|
+
|
|
550
|
+
setPrice(price: number): void {
|
|
551
|
+
this.price = price;
|
|
552
|
+
this.notify();
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Command
|
|
558
|
+
```typescript
|
|
559
|
+
interface Command { execute(): void; undo(): void; }
|
|
560
|
+
|
|
561
|
+
class AppendCommand implements Command {
|
|
562
|
+
constructor(private editor: Editor, private text: string) {}
|
|
563
|
+
execute(): void { this.editor.append(this.text); }
|
|
564
|
+
undo(): void { this.editor.delete(this.text.length); }
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
## Best Practices
|
|
569
|
+
|
|
570
|
+
- Use patterns to solve specific problems, not everywhere
|
|
571
|
+
- Combine patterns when appropriate
|
|
572
|
+
- Favor composition over inheritance
|
|
573
|
+
- Keep implementations simple
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
*Generated by aicgen*
|