@nqounetです。
いよいよ最終回です。このシリーズで私たちが作り上げてきた設計の正体を明かしましょう。
このシリーズについて
このシリーズは「Mooで覚えるオブジェクト指向プログラミング」シリーズを読了した方を対象に、実践的なドキュメント変換ツールを作りながらオブジェクト指向設計を深く学ぶシリーズです。
振り返り:何を作ってきたか

シリーズを通して、以下のような構造を作ってきました。
classDiagram
class Element {
+content
+accept(visitor)
}
class Paragraph {
+accept(visitor)
}
class Heading {
+level
+accept(visitor)
}
class CodeBlock {
+language
+accept(visitor)
}
Element <|-- Paragraph
Element <|-- Heading
Element <|-- CodeBlock
class Visitor {
+visit_heading(element)
+visit_paragraph(element)
+visit_code_block(element)
}
class HtmlConverter {
+visit_xxx methods
}
class TextConverter {
+visit_xxx methods
}
class WordCounter {
+visit_xxx methods
}
class LinkExtractor {
+visit_xxx methods
}
Visitor <|.. HtmlConverter
Visitor <|.. TextConverter
Visitor <|.. WordCounter
Visitor <|.. LinkExtractor
Element ..> Visitor : accept
これは「Visitorパターン」と呼ばれるデザインパターンです。
Visitorパターンとは
GoF(Gang of Four)による定義:
オブジェクト構造の要素に対して実行する操作を表現する。Visitorを使うことで、操作対象のクラスを変更することなく、新しい操作を定義できる。
私たちの実装に当てはめると:
| パターンの構成要素 | 私たちの実装 |
|---|---|
| Element(要素) | Element, Paragraph, Heading, CodeBlock |
| Visitor(訪問者) | HtmlConverter, TextConverter, WordCounter, LinkExtractor |
| accept() | 各要素クラスのacceptメソッド |
| visit() | visit_heading, visit_paragraph, visit_code_block |
なぜVisitorパターンなのか
このパターンの本質は「操作の分離」です。
従来のアプローチ
| |
要素に操作が密結合している:
classDiagram
class Heading {
+to_html()
+to_text()
+count_chars()
+extract_links()
}
note for Heading "操作が増えるたびに\nクラスを変更する必要あり"
Visitorパターンのアプローチ
| |
要素と操作が分離されている:
classDiagram
class Heading {
+accept(visitor)
}
class HtmlConverter {
+visit_heading()
}
class TextConverter {
+visit_heading()
}
Heading ..> HtmlConverter
Heading ..> TextConverter
note for Heading "操作が増えても\nHeadingは変更不要"
Double Dispatch(二重ディスパッチ)
Visitorパターンの核心は「Double Dispatch」です。
| |
2回のメソッド呼び出しにより、「要素の種類」と「操作の種類」の組み合わせで処理が決定されます。
SOLID原則との関係
Visitorパターンは、SOLID原則のうち特に以下の2つを実現しています。
SRP(単一責任の原則)
各Visitorクラスは1つの操作だけを担当します。
- HtmlConverter: HTML変換のみ
- WordCounter: 文字数カウントのみ
- LinkExtractor: リンク抽出のみ
OCP(開放閉鎖の原則)
新しい操作を追加する場合、既存のコードを変更せずに拡張できます。
| |
Element、Paragraph、Heading、CodeBlockは一切変更不要です。
Visitorパターンが有効な場面
このパターンが力を発揮するのは:
- オブジェクト構造(要素の種類)が安定している
- 操作の種類が頻繁に追加される
- 操作ごとにコードをまとめたい
今回のドキュメント変換ツールはまさにこの条件に当てはまります。
- 要素の種類(見出し、段落、コードブロック)は比較的安定
- 出力形式や分析処理は後からいくらでも追加したい
Visitorパターンの注意点
逆に、このパターンが不向きな場面もあります。
- 要素の種類が頻繁に変更される場合
- 新しい要素を追加すると、全てのVisitorにvisit_xxxメソッドを追加する必要がある
- オブジェクト構造が複雑でない場合
- シンプルな構造なら、if/elseで十分な場合もある
完成したドキュメント変換ツール
シリーズを通して作成した完成版です。
| |
シリーズのまとめ
このシリーズを通して、以下のことを学びました。
| 回 | 学んだこと |
|---|---|
| 第1回 | 基本のElementクラスとパース処理 |
| 第2回 | 継承による要素クラスの分離 |
| 第3回 | if/else分岐の限界(SRP/OCP違反) |
| 第4回 | Converterクラスへの処理の委譲 |
| 第5回 | accept/visitによるDouble Dispatch |
| 第6回 | OCPの実践(TextConverter追加) |
| 第7回 | 複数Visitorの共存 |
| 第8回 | Visitorパターンの正体 |
Visitorパターンは、オブジェクト構造と操作を分離することで、拡張性の高い設計を実現します。ぜひ実務でも活用してみてください。
最後まで読んでいただき、ありがとうございました!
