モダン開発で使えるデザインパターン実践ガイド - GoF to 2025

2025.12.02

公式ドキュメント

デザインパターンは、ソフトウェア設計における再利用可能な解決策のカタログです。1994年のGoF(Gang of Four)本から30年、パターンの本質は変わらずとも、その適用方法は大きく進化しました。本記事では、モダンな開発環境での実践的なパターン活用法を解説します。

デザインパターンの分類

flowchart TB
 subgraph DP["デザインパターン"]
 subgraph Creational["生成パターン"]
 C1["Singleton"]
 C2["Factory Method"]
 C3["Abstract Factory"]
 C4["Builder"]
 C5["Prototype"]
 end

 subgraph Structural["構造パターン"]
 S1["Adapter"]
 S2["Bridge"]
 S3["Composite"]
 S4["Decorator"]
 S5["Facade"]
 S6["Flyweight"]
 S7["Proxy"]
 end

 subgraph Behavioral["振る舞いパターン"]
 B1["Strategy"]
 B2["Observer"]
 B3["Command"]
 B4["State"]
 B5["Template Method"]
 B6["Iterator"]
 B7["Mediator"]
 B8["Memento"]
 B9["Visitor"]
 B10["Chain of Resp."]
 end
 end

生成パターン

Factory Method パターン

オブジェクトの生成をサブクラスに委譲し、生成ロジックを分離します。

// モダンTypeScriptでのFactory Method実装

// 製品インターフェース
interface Notification {
 send(message: string): Promise<void>;
}

// 具体的な製品
class EmailNotification implements Notification {
 constructor(private email: string) {}

 async send(message: string): Promise<void> {
 console.log(`Email to ${this.email}: ${message}`);
 // 実際のメール送信ロジック
 }
}

class SlackNotification implements Notification {
 constructor(private webhookUrl: string) {}

 async send(message: string): Promise<void> {
 console.log(`Slack webhook: ${message}`);
 // Slack API呼び出し
 }
}

class SMSNotification implements Notification {
 constructor(private phoneNumber: string) {}

 async send(message: string): Promise<void> {
 console.log(`SMS to ${this.phoneNumber}: ${message}`);
 // SMS送信API呼び出し
 }
}

// ファクトリ(関数ベース - モダンなアプローチ)
type NotificationType = 'email' | 'slack' | 'sms';

interface NotificationConfig {
 type: NotificationType;
 email?: string;
 webhookUrl?: string;
 phoneNumber?: string;
}

function createNotification(config: NotificationConfig): Notification {
 switch (config.type) {
 case 'email':
 if (!config.email) throw new Error('Email required');
 return new EmailNotification(config.email);
 case 'slack':
 if (!config.webhookUrl) throw new Error('Webhook URL required');
 return new SlackNotification(config.webhookUrl);
 case 'sms':
 if (!config.phoneNumber) throw new Error('Phone number required');
 return new SMSNotification(config.phoneNumber);
 default:
 throw new Error(`Unknown notification type: ${config.type}`);
 }
}

// 使用例
const notification = createNotification({
 type: 'email',
 email: 'user@example.com'
});
await notification.send('Hello!');

Builder パターン

複雑なオブジェクトの構築を段階的に行います。

// Builderパターン - Fluent API実装

interface HttpRequestConfig {
 method: 'GET' | 'POST' | 'PUT' | 'DELETE';
 url: string;
 headers: Record<string, string>;
 body?: unknown;
 timeout: number;
 retries: number;
}

class HttpRequestBuilder {
 private config: Partial<HttpRequestConfig> = {
 method: 'GET',
 headers: {},
 timeout: 30000,
 retries: 0,
 };

 url(url: string): this {
 this.config.url = url;
 return this;
 }

 method(method: HttpRequestConfig['method']): this {
 this.config.method = method;
 return this;
 }

 header(key: string, value: string): this {
 this.config.headers = {
 ...this.config.headers,
 [key]: value,
 };
 return this;
 }

 authorization(token: string): this {
 return this.header('Authorization', `Bearer ${token}`);
 }

 contentType(type: string): this {
 return this.header('Content-Type', type);
 }

 json(data: unknown): this {
 this.config.body = data;
 return this.contentType('application/json');
 }

 timeout(ms: number): this {
 this.config.timeout = ms;
 return this;
 }

 retries(count: number): this {
 this.config.retries = count;
 return this;
 }

 build(): HttpRequestConfig {
 if (!this.config.url) {
 throw new Error('URL is required');
 }
 return this.config as HttpRequestConfig;
 }

 // 便利メソッド - 直接実行
 async execute<T>(): Promise<T> {
 const config = this.build();
 // 実際のfetch実行ロジック
 const response = await fetch(config.url, {
 method: config.method,
 headers: config.headers,
 body: config.body ? JSON.stringify(config.body) : undefined,
 });
 return response.json();
 }
}

// 使用例
const response = await new HttpRequestBuilder()
 .url('https://api.example.com/users')
 .method('POST')
 .authorization('my-token')
 .json({ name: 'John', email: 'john@example.com' })
 .timeout(5000)
 .retries(3)
 .execute<{ id: string }>();

Singleton パターン(モダンな代替案)

従来のSingletonは避け、DIコンテナやモジュールスコープを活用します。

// ❌ 従来のSingleton(避けるべき)
class LegacySingleton {
 private static instance: LegacySingleton;

 private constructor() {}

 static getInstance(): LegacySingleton {
 if (!LegacySingleton.instance) {
 LegacySingleton.instance = new LegacySingleton();
 }
 return LegacySingleton.instance;
 }
}

// ✅ モジュールスコープでのシングルトン
// database.ts
class DatabaseConnection {
 constructor(private connectionString: string) {}

 async query<T>(sql: string): Promise<T[]> {
 // クエリ実行
 return [];
 }
}

// モジュールレベルでエクスポート
export const db = new DatabaseConnection(process.env.DATABASE_URL!);

// ✅ DIコンテナを使用(推奨)
// container.ts
import { Container } from 'inversify';

const container = new Container();
container.bind<DatabaseConnection>('Database')
 .to(DatabaseConnection)
 .inSingletonScope();

export { container };

構造パターン

Adapter パターン

互換性のないインターフェース間を橋渡しします。

// レガシーAPIを新しいインターフェースに適合させる

// 既存のレガシーシステム
interface LegacyPaymentSystem {
 processPayment(
 amount: number,
 cardNumber: string,
 expiry: string,
 cvv: string
 ): boolean;
}

class LegacyStripePayment implements LegacyPaymentSystem {
 processPayment(
 amount: number,
 cardNumber: string,
 expiry: string,
 cvv: string
 ): boolean {
 console.log('Processing via legacy Stripe...');
 return true;
 }
}

// 新しいインターフェース
interface PaymentGateway {
 charge(payment: PaymentDetails): Promise<PaymentResult>;
}

interface PaymentDetails {
 amount: number;
 currency: string;
 card: {
 number: string;
 expiryMonth: number;
 expiryYear: number;
 cvc: string;
 };
}

interface PaymentResult {
 success: boolean;
 transactionId: string;
 error?: string;
}

// アダプター
class LegacyPaymentAdapter implements PaymentGateway {
 constructor(private legacySystem: LegacyPaymentSystem) {}

 async charge(payment: PaymentDetails): Promise<PaymentResult> {
 const expiry = `${payment.card.expiryMonth}/${payment.card.expiryYear}`;

 const success = this.legacySystem.processPayment(
 payment.amount,
 payment.card.number,
 expiry,
 payment.card.cvc
 );

 return {
 success,
 transactionId: success ? crypto.randomUUID() : '',
 error: success ? undefined : 'Payment failed',
 };
 }
}

// 使用例
const legacyStripe = new LegacyStripePayment();
const paymentGateway: PaymentGateway = new LegacyPaymentAdapter(legacyStripe);

const result = await paymentGateway.charge({
 amount: 1000,
 currency: 'JPY',
 card: {
 number: '4242424242424242',
 expiryMonth: 12,
 expiryYear: 2025,
 cvc: '123',
 },
});

Decorator パターン

既存オブジェクトに動的に機能を追加します。

// TypeScriptデコレータを使用した実装

// メソッドデコレータ - ログ出力
function Log(
 target: object,
 propertyKey: string,
 descriptor: PropertyDescriptor
): PropertyDescriptor {
 const originalMethod = descriptor.value;

 descriptor.value = async function (...args: unknown[]) {
 console.log(`[${propertyKey}] Called with:`, args);
 const start = performance.now();

 try {
 const result = await originalMethod.apply(this, args);
 const duration = performance.now() - start;
 console.log(`[${propertyKey}] Returned:`, result, `(${duration}ms)`);
 return result;
 } catch (error) {
 console.error(`[${propertyKey}] Error:`, error);
 throw error;
 }
 };

 return descriptor;
}

// キャッシュデコレータ
function Cache(ttlMs: number = 60000) {
 const cache = new Map<string, { value: unknown; expiry: number }>();

 return function (
 target: object,
 propertyKey: string,
 descriptor: PropertyDescriptor
 ): PropertyDescriptor {
 const originalMethod = descriptor.value;

 descriptor.value = async function (...args: unknown[]) {
 const key = JSON.stringify(args);
 const cached = cache.get(key);

 if (cached && cached.expiry > Date.now()) {
 console.log(`[Cache Hit] ${propertyKey}`);
 return cached.value;
 }

 const result = await originalMethod.apply(this, args);
 cache.set(key, { value: result, expiry: Date.now() + ttlMs });
 return result;
 };

 return descriptor;
 };
}

// リトライデコレータ
function Retry(maxAttempts: number = 3, delayMs: number = 1000) {
 return function (
 target: object,
 propertyKey: string,
 descriptor: PropertyDescriptor
 ): PropertyDescriptor {
 const originalMethod = descriptor.value;

 descriptor.value = async function (...args: unknown[]) {
 let lastError: Error;

 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
 try {
 return await originalMethod.apply(this, args);
 } catch (error) {
 lastError = error as Error;
 console.warn(`[Retry] Attempt ${attempt}/${maxAttempts} failed`);

 if (attempt < maxAttempts) {
 await new Promise(resolve => setTimeout(resolve, delayMs));
 }
 }
 }

 throw lastError!;
 };

 return descriptor;
 };
}

// デコレータの適用
class UserService {
 @Log
 @Cache(30000) // 30秒キャッシュ
 @Retry(3, 1000)
 async getUser(id: string): Promise<User> {
 const response = await fetch(`/api/users/${id}`);
 return response.json();
 }
}

Proxy パターン

オブジェクトへのアクセスを制御します。

// ES6 Proxyを使用した実装

interface User {
 id: string;
 name: string;
 email: string;
 role: 'admin' | 'user';
}

// バリデーションプロキシ
function createValidatedUser(user: User): User {
 return new Proxy(user, {
 set(target, property, value) {
 if (property === 'email') {
 if (typeof value !== 'string' || !value.includes('@')) {
 throw new Error('Invalid email format');
 }
 }

 if (property === 'role') {
 if (!['admin', 'user'].includes(value)) {
 throw new Error('Invalid role');
 }
 }

 return Reflect.set(target, property, value);
 },
 });
}

// 遅延ロードプロキシ
function createLazyLoader<T extends object>(
 loader: () => Promise<T>
): T {
 let instance: T | null = null;
 let loading: Promise<T> | null = null;

 return new Proxy({} as T, {
 get(target, property) {
 if (!instance) {
 if (!loading) {
 loading = loader().then(loaded => {
 instance = loaded;
 return loaded;
 });
 }
 // プロミスを返すか、ブロックするか選択
 return loading.then(inst => (inst as any)[property]);
 }
 return (instance as any)[property];
 },
 });
}

// アクセス制御プロキシ
function createSecureObject<T extends object>(
 obj: T,
 currentUser: User
): T {
 return new Proxy(obj, {
 get(target, property) {
 const value = Reflect.get(target, property);

 // 管理者限定プロパティ
 const adminOnlyProps = ['password', 'secretKey', 'apiToken'];
 if (adminOnlyProps.includes(String(property))) {
 if (currentUser.role !== 'admin') {
 throw new Error('Access denied: Admin only');
 }
 }

 return value;
 },

 set(target, property, value) {
 if (currentUser.role !== 'admin') {
 throw new Error('Access denied: Read only');
 }
 return Reflect.set(target, property, value);
 },
 });
}

振る舞いパターン

Strategy パターン

アルゴリズムをカプセル化し、交換可能にします。

// 料金計算ストラテジー

interface PricingStrategy {
 calculatePrice(basePrice: number, quantity: number): number;
 getName(): string;
}

class RegularPricing implements PricingStrategy {
 calculatePrice(basePrice: number, quantity: number): number {
 return basePrice * quantity;
 }
 getName(): string {
 return 'Regular';
 }
}

class BulkPricing implements PricingStrategy {
 constructor(private discountThreshold: number, private discountRate: number) {}

 calculatePrice(basePrice: number, quantity: number): number {
 if (quantity >= this.discountThreshold) {
 return basePrice * quantity * (1 - this.discountRate);
 }
 return basePrice * quantity;
 }

 getName(): string {
 return `Bulk (${this.discountRate * 100}% off for ${this.discountThreshold}+)`;
 }
}

class SubscriberPricing implements PricingStrategy {
 constructor(private memberDiscountRate: number) {}

 calculatePrice(basePrice: number, quantity: number): number {
 return basePrice * quantity * (1 - this.memberDiscountRate);
 }

 getName(): string {
 return `Subscriber (${this.memberDiscountRate * 100}% off)`;
 }
}

class SeasonalPricing implements PricingStrategy {
 constructor(
 private seasonalMultiplier: number,
 private seasonName: string
 ) {}

 calculatePrice(basePrice: number, quantity: number): number {
 return basePrice * quantity * this.seasonalMultiplier;
 }

 getName(): string {
 return `Seasonal - ${this.seasonName}`;
 }
}

// コンテキスト
class ShoppingCart {
 private items: Array<{ name: string; price: number; quantity: number }> = [];
 private pricingStrategy: PricingStrategy = new RegularPricing();

 addItem(name: string, price: number, quantity: number): void {
 this.items.push({ name, price, quantity });
 }

 setPricingStrategy(strategy: PricingStrategy): void {
 this.pricingStrategy = strategy;
 }

 calculateTotal(): number {
 return this.items.reduce((total, item) => {
 return total + this.pricingStrategy.calculatePrice(item.price, item.quantity);
 }, 0);
 }

 getReceipt(): string {
 const lines = this.items.map(item => {
 const subtotal = this.pricingStrategy.calculatePrice(item.price, item.quantity);
 return `${item.name} x${item.quantity}: ¥${subtotal}`;
 });

 return [
 `Pricing: ${this.pricingStrategy.getName()}`,
 '---',
 ...lines,
 '---',
 `Total: ¥${this.calculateTotal()}`,
 ].join('\n');
 }
}

// 使用例
const cart = new ShoppingCart();
cart.addItem('Widget', 1000, 5);
cart.addItem('Gadget', 2000, 3);

console.log(cart.getReceipt());
// Pricing: Regular
// Total: ¥11000

cart.setPricingStrategy(new BulkPricing(3, 0.15));
console.log(cart.getReceipt());
// Pricing: Bulk (15% off for 3+)
// Total: ¥9350

Observer パターン

オブジェクト間の一対多の依存関係を定義します。

// TypeScriptでの型安全なObserver実装

type EventMap = {
 userCreated: { id: string; email: string };
 userUpdated: { id: string; changes: Partial<User> };
 userDeleted: { id: string };
 orderPlaced: { orderId: string; userId: string; total: number };
};

type EventKey = keyof EventMap;
type EventHandler<K extends EventKey> = (event: EventMap[K]) => void | Promise<void>;

class TypedEventEmitter {
 private handlers = new Map<EventKey, Set<EventHandler<any>>>();

 on<K extends EventKey>(event: K, handler: EventHandler<K>): () => void {
 if (!this.handlers.has(event)) {
 this.handlers.set(event, new Set());
 }
 this.handlers.get(event)!.add(handler);

 // 購読解除関数を返す
 return () => this.off(event, handler);
 }

 off<K extends EventKey>(event: K, handler: EventHandler<K>): void {
 this.handlers.get(event)?.delete(handler);
 }

 async emit<K extends EventKey>(event: K, data: EventMap[K]): Promise<void> {
 const eventHandlers = this.handlers.get(event);
 if (!eventHandlers) return;

 const promises = Array.from(eventHandlers).map(handler =>
 Promise.resolve(handler(data))
 );

 await Promise.all(promises);
 }

 once<K extends EventKey>(event: K, handler: EventHandler<K>): () => void {
 const wrappedHandler: EventHandler<K> = async (data) => {
 this.off(event, wrappedHandler);
 await handler(data);
 };
 return this.on(event, wrappedHandler);
 }
}

// 使用例
const eventBus = new TypedEventEmitter();

// メール送信サービス
eventBus.on('userCreated', async ({ email }) => {
 console.log(`Sending welcome email to ${email}`);
});

// 分析サービス
eventBus.on('userCreated', ({ id }) => {
 console.log(`Tracking user creation: ${id}`);
});

// 注文処理
eventBus.on('orderPlaced', async ({ orderId, userId, total }) => {
 console.log(`Processing order ${orderId} for user ${userId}: ¥${total}`);
});

// イベント発火
await eventBus.emit('userCreated', {
 id: 'user-123',
 email: 'user@example.com',
});

Command パターン

操作をオブジェクトとしてカプセル化します。

// Undo/Redo機能を持つCommand実装

interface Command {
 execute(): Promise<void>;
 undo(): Promise<void>;
 getDescription(): string;
}

// テキストエディタのコマンド例
class TextEditor {
 private content: string = '';

 getContent(): string {
 return this.content;
 }

 setContent(content: string): void {
 this.content = content;
 }

 insertAt(position: number, text: string): void {
 this.content =
 this.content.slice(0, position) + text + this.content.slice(position);
 }

 deleteRange(start: number, end: number): string {
 const deleted = this.content.slice(start, end);
 this.content = this.content.slice(0, start) + this.content.slice(end);
 return deleted;
 }
}

class InsertTextCommand implements Command {
 constructor(
 private editor: TextEditor,
 private position: number,
 private text: string
 ) {}

 async execute(): Promise<void> {
 this.editor.insertAt(this.position, this.text);
 }

 async undo(): Promise<void> {
 this.editor.deleteRange(this.position, this.position + this.text.length);
 }

 getDescription(): string {
 return `Insert "${this.text}" at position ${this.position}`;
 }
}

class DeleteTextCommand implements Command {
 private deletedText: string = '';

 constructor(
 private editor: TextEditor,
 private start: number,
 private end: number
 ) {}

 async execute(): Promise<void> {
 this.deletedText = this.editor.deleteRange(this.start, this.end);
 }

 async undo(): Promise<void> {
 this.editor.insertAt(this.start, this.deletedText);
 }

 getDescription(): string {
 return `Delete from ${this.start} to ${this.end}`;
 }
}

// コマンドマネージャー(Invoker)
class CommandManager {
 private history: Command[] = [];
 private redoStack: Command[] = [];

 async execute(command: Command): Promise<void> {
 await command.execute();
 this.history.push(command);
 this.redoStack = []; // 新しいコマンド実行でredoスタックをクリア
 }

 async undo(): Promise<boolean> {
 const command = this.history.pop();
 if (!command) return false;

 await command.undo();
 this.redoStack.push(command);
 return true;
 }

 async redo(): Promise<boolean> {
 const command = this.redoStack.pop();
 if (!command) return false;

 await command.execute();
 this.history.push(command);
 return true;
 }

 getHistory(): string[] {
 return this.history.map(cmd => cmd.getDescription());
 }
}

// 使用例
const editor = new TextEditor();
const manager = new CommandManager();

await manager.execute(new InsertTextCommand(editor, 0, 'Hello '));
await manager.execute(new InsertTextCommand(editor, 6, 'World!'));
console.log(editor.getContent()); // "Hello World!"

await manager.undo();
console.log(editor.getContent()); // "Hello "

await manager.redo();
console.log(editor.getContent()); // "Hello World!"

ポイント: GoFパターンの多くはクラスベースですが、モダンな開発では関数型アプローチや組み込み言語機能で代替できるものも多くあります。パターンの「意図」を理解し、適切な手段で実現しましょう

現代的なアーキテクチャパターン

Repository パターン

データアクセスロジックを抽象化します。

// Repositoryパターン with TypeScript

interface Entity {
 id: string;
}

interface Repository<T extends Entity> {
 findById(id: string): Promise<T | null>;
 findAll(): Promise<T[]>;
 findBy(criteria: Partial<T>): Promise<T[]>;
 save(entity: T): Promise<T>;
 delete(id: string): Promise<boolean>;
}

interface User extends Entity {
 id: string;
 email: string;
 name: string;
 createdAt: Date;
}

// インメモリ実装(テスト用)
class InMemoryUserRepository implements Repository<User> {
 private users: Map<string, User> = new Map();

 async findById(id: string): Promise<User | null> {
 return this.users.get(id) || null;
 }

 async findAll(): Promise<User[]> {
 return Array.from(this.users.values());
 }

 async findBy(criteria: Partial<User>): Promise<User[]> {
 return Array.from(this.users.values()).filter(user =>
 Object.entries(criteria).every(
 ([key, value]) => user[key as keyof User] === value
 )
 );
 }

 async save(entity: User): Promise<User> {
 this.users.set(entity.id, entity);
 return entity;
 }

 async delete(id: string): Promise<boolean> {
 return this.users.delete(id);
 }
}

// Prisma実装(本番用)
class PrismaUserRepository implements Repository<User> {
 constructor(private prisma: PrismaClient) {}

 async findById(id: string): Promise<User | null> {
 return this.prisma.user.findUnique({ where: { id } });
 }

 async findAll(): Promise<User[]> {
 return this.prisma.user.findMany();
 }

 async findBy(criteria: Partial<User>): Promise<User[]> {
 return this.prisma.user.findMany({ where: criteria });
 }

 async save(entity: User): Promise<User> {
 return this.prisma.user.upsert({
 where: { id: entity.id },
 update: entity,
 create: entity,
 });
 }

 async delete(id: string): Promise<boolean> {
 try {
 await this.prisma.user.delete({ where: { id } });
 return true;
 } catch {
 return false;
 }
 }
}

Unit of Work パターン

トランザクション内の変更を追跡・管理します。

// Unit of Work実装

interface UnitOfWork {
 begin(): Promise<void>;
 commit(): Promise<void>;
 rollback(): Promise<void>;
 userRepository: Repository<User>;
 orderRepository: Repository<Order>;
}

class PrismaUnitOfWork implements UnitOfWork {
 private transaction: Prisma.TransactionClient | null = null;
 private _userRepository: Repository<User> | null = null;
 private _orderRepository: Repository<Order> | null = null;

 constructor(private prisma: PrismaClient) {}

 async begin(): Promise<void> {
 // Prismaの対話型トランザクション
 return new Promise((resolve) => {
 this.prisma.$transaction(async (tx) => {
 this.transaction = tx;
 resolve();
 // トランザクションは commit/rollback まで保持
 });
 });
 }

 get userRepository(): Repository<User> {
 if (!this._userRepository) {
 this._userRepository = new PrismaUserRepository(
 this.transaction || this.prisma
 );
 }
 return this._userRepository;
 }

 get orderRepository(): Repository<Order> {
 if (!this._orderRepository) {
 this._orderRepository = new PrismaOrderRepository(
 this.transaction || this.prisma
 );
 }
 return this._orderRepository;
 }

 async commit(): Promise<void> {
 // Prismaトランザクションは自動コミット
 this.transaction = null;
 }

 async rollback(): Promise<void> {
 throw new Error('Rollback requested');
 }
}

// 使用例
async function createOrderWithUser(uow: UnitOfWork) {
 await uow.begin();

 try {
 const user = await uow.userRepository.save({
 id: crypto.randomUUID(),
 email: 'new@example.com',
 name: 'New User',
 createdAt: new Date(),
 });

 const order = await uow.orderRepository.save({
 id: crypto.randomUUID(),
 userId: user.id,
 total: 5000,
 status: 'pending',
 });

 await uow.commit();
 return { user, order };
 } catch (error) {
 await uow.rollback();
 throw error;
 }
}

注意: デザインパターンの過度な適用はコードを不必要に複雑にします。「パターンのためのパターン」にならないよう、実際の問題に対して適用しましょう。

実践メモ: パターン選択に迷ったら、まずSOLID原則に違反していないかを確認してください。SOLID準拠のコードは自然とパターンが適用されやすい構造になります。

SOLID原則との関係

flowchart LR
 subgraph SOLID["SOLID原則"]
 S["S - 単一責任原則 (SRP)"]
 O["O - オープン・クローズド原則 (OCP)"]
 L["L - リスコフの置換原則 (LSP)"]
 I["I - インターフェース分離原則 (ISP)"]
 D["D - 依存性逆転原則 (DIP)"]
 end

 subgraph Patterns["関連パターン"]
 P1["Factory, Strategy, Command"]
 P2["Strategy, Decorator, Template Method"]
 P3["Factory Method, Abstract Factory"]
 P4["Adapter, Facade"]
 P5["Repository, Dependency Injection"]
 end

 S --> P1
 O --> P2
 L --> P3
 I --> P4
 D --> P5

まとめ

デザインパターンは、問題解決のための共通言語であり、チーム開発における認識合わせにも役立ちます。

選定の指針

目的推奨パターン
オブジェクト生成の柔軟性Factory, Builder
既存コードの拡張Decorator, Adapter
アルゴリズムの切り替えStrategy
イベント駆動Observer
操作の取り消しCommand
データアクセス抽象化Repository

モダン開発でのベストプラクティス

  1. パターンの過剰適用を避ける - シンプルな問題にはシンプルな解決策
  2. 言語機能を活用 - TypeScript/Pythonの型システム、デコレータ
  3. テスタビリティを重視 - DIを活用し依存を注入可能に
  4. 関数型アプローチとの併用 - 状態を持たない処理は関数で

デザインパターンは目的ではなく手段です。問題を理解し、適切なパターンを選択することが重要です

参考リンク

参考リソース

← 一覧に戻る
PR
PR
PR
PR