@nqounetです。
前回は、acceptメソッドとvisit_*メソッドを使った「Double Dispatch」の仕組みを導入しました。今回は、この設計の真価を発揮します。新しい出力形式を追加してみましょう。
このシリーズについて
このシリーズは「Mooで覚えるオブジェクト指向プログラミング」シリーズを読了した方を対象に、実践的なドキュメント変換ツールを作りながらオブジェクト指向設計を深く学ぶシリーズです。
TextConverterを追加する

Markdownをプレーンテキストに変換するTextConverterを作成しましょう。
| |
これだけです。
既存のコードを変更したか?
確認してみましょう。
| クラス | 変更あり? |
|---|---|
| Element | なし |
| Paragraph | なし |
| Heading | なし |
| CodeBlock | なし |
| Parser | なし |
| HtmlConverter | なし |
| TextConverter | 新規追加 |
既存のコードを一切変更せずに、新しい機能(テキスト変換)を追加できました。これがOCP(開放閉鎖の原則)です。
使ってみよう
| |
実行結果:
| |
同じ要素に対して、異なる変換結果が得られています。
クラス構造を確認
現在のクラス構造を図にしてみましょう。
classDiagram
class Element {
+content
+accept(visitor)*
}
class Paragraph {
+accept(visitor)
}
class Heading {
+level
+accept(visitor)
}
class CodeBlock {
+language
+accept(visitor)
}
Element <|-- Paragraph : extends
Element <|-- Heading : extends
Element <|-- CodeBlock : extends
class HtmlConverter {
+visit_heading(element)
+visit_paragraph(element)
+visit_code_block(element)
}
class TextConverter {
+visit_heading(element)
+visit_paragraph(element)
+visit_code_block(element)
}
Paragraph ..> HtmlConverter : accept
Paragraph ..> TextConverter : accept
Heading ..> HtmlConverter : accept
Heading ..> TextConverter : accept
CodeBlock ..> HtmlConverter : accept
CodeBlock ..> TextConverter : accept
要素クラス(左側)と変換クラス(右側)が分離されていて、組み合わせ自由になっています。
if/elseアプローチとの比較
第3回で見たif/elseアプローチと比較してみましょう。
if/elseアプローチで出力形式を追加する場合
| |
今回のアプローチで出力形式を追加する場合
| |
今回のアプローチの方が、以下の点で優れています。
- 既存のコードを変更しない(バグを入れるリスクがない)
- 新機能が1つのクラスに集約されている(保守しやすい)
- テストが書きやすい(TextConverterだけをテストできる)
今回のポイント
今回は以下のことを学びました。
- TextConverterの追加で新しい出力形式を実現
- 既存コードを変更せずに機能拡張(OCP)
- 要素と変換処理の組み合わせが自由自在
今回の完成コード
以下が今回作成したコードの完成版です。
| |
次回予告
次回は、さらに別の種類の操作を追加します。変換だけでなく、「単語数カウント」や「リンク抽出」といった分析系の操作も、同じ仕組みで追加できることを確認しましょう。複数のVisitorが共存する世界を体験します。
お楽しみに!
