GoFデザインパターン23個を完全に理解するブログ #21 Strategy(ストラテジー)

2025/11/16

プログラミング

t f B! P L
eyecatch Strategyパターンは「アルゴリズム(処理手順)」を切り替え可能にするデザインパターン。 クラスの中で「条件分岐(if/switch)」を増やさずに、動作を差し替えることができる。 「使う側(コンテキスト)」と「アルゴリズムの実装(ストラテジー)」を分離するのが特徴。

構成

定義 説明
Strategy(戦略インターフェース) アルゴリズム共通のメソッド定義。
ConcreteStrategy(具体的戦略) 異なるアルゴリズムをそれぞれ実装。
Context(文脈) どのStrategyを使うかを外部から設定して利用。

サンプルコード

支払い方法を切り替えるパターンのサンプルコードです。

Strategyインターフェース

class PaymentStrategy { pay(amount) { throw new Error("実装してください"); } }

Concrete Strategies

class CreditCardPayment extends PaymentStrategy { pay(amount) { console.log(`💳 クレジットカードで${amount}円を支払いました`); } } class PayPalPayment extends PaymentStrategy { pay(amount) { console.log(`💻 PayPalで${amount}円を支払いました`); } } class BankTransferPayment extends PaymentStrategy { pay(amount) { console.log(`🏦 銀行振込で${amount}円を支払いました`); } }

Context

class ShoppingCart { constructor(strategy) { this.strategy = strategy; } checkout(amount) { this.strategy.pay(amount); } }

利用例

const cart1 = new ShoppingCart(new CreditCardPayment()); cart1.checkout(1000); const cart2 = new ShoppingCart(new PayPalPayment()); cart2.checkout(2000);

メリット

・アルゴリズムを動的に切り替え可能 ・if/switchの乱用を防げる ・オープン/クローズド原則(OCP)に従う(新しい戦略を追加しても既存コードに影響しない)

デメリット

・クラス数が増える(戦略ごとに1クラス) ・単純な処理では設計が大げさになりがち

使いどころ

・条件分岐でアルゴリズムを切り替えている部分をポリモーフィズム化したいとき ・「並べ替え方」「圧縮方式」「支払い手段」などを動的に変更したい場合

canvasデモ

Canvas上で「描画ストラテジーを切り替える」Strategyパターンのデモ <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Strategy Pattern Canvas Demo</title> <style> body { font-family: sans-serif; text-align: center; margin-top: 20px; } canvas { border: 1px solid #999; margin-top: 10px; background: #fafafa; } button { margin: 5px; padding: 8px 16px; border-radius: 8px; cursor: pointer; } </style> </head> <body> <h2>🎨 Strategyパターン: 描画スタイル切り替え</h2> <div> <button id="circleBtn">円を描く</button> <button id="squareBtn">四角を描く</button> <button id="triangleBtn">三角を描く</button> </div> <canvas id="canvas" width="400" height="300"></canvas> <script> // === Strategyインターフェース === class DrawStrategy { draw(ctx) { throw new Error("draw()を実装してください"); } } // === Concrete Strategies === class CircleStrategy extends DrawStrategy { draw(ctx) { ctx.beginPath(); ctx.arc(200, 150, 80, 0, Math.PI * 2); ctx.fillStyle = "#ff6666"; ctx.fill(); ctx.stroke(); } } class SquareStrategy extends DrawStrategy { draw(ctx) { ctx.fillStyle = "#66ccff"; ctx.fillRect(120, 80, 160, 160); ctx.strokeRect(120, 80, 160, 160); } } class TriangleStrategy extends DrawStrategy { draw(ctx) { ctx.beginPath(); ctx.moveTo(200, 60); ctx.lineTo(120, 240); ctx.lineTo(280, 240); ctx.closePath(); ctx.fillStyle = "#99cc66"; ctx.fill(); ctx.stroke(); } } // === Context === class CanvasContext { constructor(canvas) { this.ctx = canvas.getContext("2d"); this.strategy = null; } setStrategy(strategy) { this.strategy = strategy; this.redraw(); } clear() { this.ctx.clearRect(0, 0, 400, 300); } redraw() { this.clear(); if (this.strategy) { this.strategy.draw(this.ctx); } } } // === 実行部 === const canvas = document.getElementById("canvas"); const painter = new CanvasContext(canvas); document.getElementById("circleBtn").onclick = () => painter.setStrategy(new CircleStrategy()); document.getElementById("squareBtn").onclick = () => painter.setStrategy(new SquareStrategy()); document.getElementById("triangleBtn").onclick = () => painter.setStrategy(new TriangleStrategy()); </script> </body> </html>

あとがき

同じメソッド内で、条件分岐が増えると、ネストが深くなる場合があります。 また、条件が複雑なほど、プログラム自体の理解も難しくなります。 クラスに分けることで、役割ごとの分岐を最小限に抑えて、分かりやすいプログラムコードになります。 ストラテジーパターンは、そうしたコードの難読性を和らげる効果を設計することが可能になるパターンですね。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ