GoFデザインパターン23個を完全に理解するブログ #22 Template Method(テンプレートメソッド)

2025/11/17

プログラミング

t f B! P L
eyecatch Template Method(テンプレートメソッド)は、「処理の流れ(テンプレート)」を親クラスに定義し、その中の一部の処理だけをサブクラスに任せるパターンです。 「手順の型は同じだけど、細部だけ変えたい」場合に最適なパターンですね。 料理の手順に例えるなら、 共通(材料を準備 → 加熱 → 盛り付け)の順に処理をする際、 料理の種類(カレー・ラーメンなど) によって中身は違うというような場合に。 共通の流れはテンプレート化して、差分だけを各サブクラスに任せるようにする。

構成

Abstract Class(抽象クラス)

・一連の処理の流れ(テンプレートメソッド)を定義。 ・一部のメソッドを抽象化して、サブクラスが実装。

Concrete Class(具象クラス)

・抽象メソッドを具体的に実装。

サンプルコード1

料理テンプレート

抽象クラス

class CookingTemplate { cook() { this.prepare(); this.heat(); this.serve(); } prepare() { throw new Error("prepare()を実装してください"); } heat() { throw new Error("heat()を実装してください"); } serve() { console.log("料理をお皿に盛り付けました!"); } }

具象クラス1

class CurryCooking extends CookingTemplate { prepare() { console.log("カレーの材料を切る"); } heat() { console.log("カレーを煮込む"); } }

具象クラス2

class RamenCooking extends CookingTemplate { prepare() { console.log("ラーメンの具材を準備"); } heat() { console.log("麺を茹でてスープを温める"); } }

実行

const curry = new CurryCooking(); curry.cook(); // => カレーの材料を切る // => カレーを煮込む // => 料理をお皿に盛り付けました! const ramen = new RamenCooking(); ramen.cook(); // => ラーメンの具材を準備 // => 麺を茹でてスープを温める // => 料理をお皿に盛り付けました!

サンプルコード2

Canvasでテンプレートメソッドを可視化するデモ <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Template Method 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>🧩 Template Methodパターン: 描画テンプレート</h2> <div> <button id="circleBtn">円を描く</button> <button id="squareBtn">四角を描く</button> <button id="triangleBtn">三角を描く</button> </div> <canvas id="canvas" width="400" height="300"></canvas> <script> // === 抽象クラス(テンプレート) === class ShapeTemplate { constructor(ctx) { this.ctx = ctx; } // Template Method:共通の処理の流れ draw() { this.clear(); this.drawShape(); this.drawLabel(); } clear() { this.ctx.clearRect(0, 0, 400, 300); } // サブクラスに任せる抽象メソッド drawShape() { throw new Error("drawShape()を実装してください"); } drawLabel() { this.ctx.font = "18px sans-serif"; this.ctx.fillStyle = "#333"; this.ctx.textAlign = "center"; this.ctx.fillText(this.constructor.name.replace("Shape", ""), 200, 270); } } // === 具象クラス1 === class CircleShape extends ShapeTemplate { drawShape() { const ctx = this.ctx; ctx.beginPath(); ctx.arc(200, 140, 60, 0, Math.PI * 2); ctx.fillStyle = "#ff6666"; ctx.fill(); ctx.stroke(); } } // === 具象クラス2 === class SquareShape extends ShapeTemplate { drawShape() { const ctx = this.ctx; ctx.fillStyle = "#66ccff"; ctx.fillRect(130, 80, 140, 140); ctx.strokeRect(130, 80, 140, 140); } } // === 具象クラス3 === class TriangleShape extends ShapeTemplate { drawShape() { const ctx = this.ctx; ctx.beginPath(); ctx.moveTo(200, 60); ctx.lineTo(120, 220); ctx.lineTo(280, 220); ctx.closePath(); ctx.fillStyle = "#99cc66"; ctx.fill(); ctx.stroke(); } } // === 実行環境 === const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); document.getElementById("circleBtn").onclick = () => new CircleShape(ctx).draw(); document.getElementById("squareBtn").onclick = () => new SquareShape(ctx).draw(); document.getElementById("triangleBtn").onclick = () => new TriangleShape(ctx).draw(); </script> </body> </html>

ポイント解説

Template Method: draw() が「共通の手順」を定義(clear → drawShape → drawLabel) 差分部分: drawShape() を各サブクラスが独自に実装。 結果: どの図形でも同じ流れで描かれるが、 形状(中身)だけが変化する。

メリット

・共通処理の流れを1か所で管理できる。 ・処理の変更や追加が容易(サブクラス単位で拡張できる)。 ・コードの重複が減る。

デメリット

・継承ベースなので、柔軟な差し替え(動的切り替え) が難しい。 ・サブクラスが増えると管理が煩雑になる。

よく使う場面

一連の流れが固定で、部分的に差を出したい処理。
・ファイル入出力(共通:open → read/write → close) ・HTMLレンダリング(共通:header → body → footer) ・テスト実行(共通:setup → run → teardown)

あとがき

類似のclass処理で、構成をルール化して、同じメソッドを用意した状態で、それを手順通りに処理するのがテンプレートパターンですね。 手順の構成をしっかりと設計できていないと、エラーにつながりますが、システムの管理は便利になることが分かります。 でも、手順が変わると、全ての手順メソッドを同率に更新する必要があるので、更新管理はもしかしたら一手間かかるかもしれません。

人気の投稿

このブログを検索

ごあいさつ

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

ブログ アーカイブ