文書コンバータは数学だった。
このワークシートはMath by Codeの一部です。
ビルドパタン:1つの作成で多様な表現形式のオブジェクトを作る。
たとえば、
タイトル、文章、箇条書き、数式がある文書ページを作りたい。
しかし、テキストファイルのベタ打ちに近いものの欲しいし、
あとで、図形を入れるかもしれないから、ワード文書にもしたい。
でも、ネットにのせには、HTML形式のページにもできたらいいなあ。
さらに、PDFファイルで保存できたら便利かも。。。。。
これは数学に限らず、仕事でも、趣味でもよくあることですね。
文書コンバートは、いろんなソフトで可能になっています。
これが、文書コンバータ、ビルドパタンです。
今回は、困った状況の脱出というお話ではなく、
「よくある優れた機能の裏側(構造)を覗き見よう」というテーマです。
1.文書コンバータパタン
フレームワーク会社Docに登場するのは2人です。
1人はディレクターで文書の構成と内容を決めます。
もう1人は文書の要素を決める名誉職人です。
ユーザが自分の欲しい表現形式の職人スタッフを準備することになるでしょう。
Doc社の名誉職人は手を汚しません。名前はビルドさんです。
文書の構成要素を抽象的に提示する、抽象契約者です。AbstractFactoryですね。
Doc社のディレクターは、名誉職人の提示したキーワードにそって
文章を構成し、文字内容を決めるでしょう。
ユーザは、AbstractFactoryの下請け工場を作ることになります。
テキストファイル、HTMLファイル。
とりあえずこの2タイプがページができるように実装してみましょう。
2.文書コンバータを実装する
[IN]build.py
# ==========================================
# 【クラスC:名誉職人】ビルドさん(抽象契約)
# ==========================================
class AbstractDocBuilder:
"""【名誉職人】一切手は汚さない。
ただ、文書を構成するためのキーワード(インターフェース)を提示するだけ。"""
def add_title(self, text): raise NotImplementedError()
def add_paragraph(self, text): raise NotImplementedError()
def add_bullet_point(self, item): raise NotImplementedError()
def add_formula(self, expr): raise NotImplementedError()
def get_result(self): raise NotImplementedError()
# ==========================================
# 【クラスA:ディレクター】編集長
# ==========================================
class DocumentDirector:
"""【ディレクター】文書の構成(順番)と内容(文字)を決定する。
ただし、最終的にそれがどんな形式(表現)になるかは『知らない』。"""
def __init__(self, builder: AbstractDocBuilder):
self.builder = builder # ユーザーが連れてきた具象職人をセットする
def construct_page(self):
# 名誉職人のキーワードに沿って、上から順番に原稿を組み立てる
self.builder.add_title("デザインパターンを学ぼう")
self.builder.add_paragraph("二元論で語ることは簡単だ。")
self.builder.add_bullet_point("下請け工場パタン")
self.builder.add_bullet_point("コピー屋さんパタン")
self.builder.add_formula("E = mc^2")
# ==========================================
# 【カスタマイズ1】テキストファイル担当の職人
# ==========================================
class TextDocBuilder(AbstractDocBuilder):
def __init__(self):
self.lines = [] # ここにテキストを書き溜めていく(手を汚す場所)
def add_title(self, text):
self.lines.append(f"【★ {text} ★】\n")
def add_paragraph(self, text):
self.lines.append(f" {text}\n")
def add_bullet_point(self, item):
self.lines.append(f" ・ {item}\n")
def add_formula(self, expr):
self.lines.append(f" [式: {expr}]\n")
def get_result(self):
# 最後に、溜まった文字列をガッチャンコして完成品を渡す
return "".join(self.lines)
# ==========================================
# 【カスタマイズ2】HTMLファイル担当の職人
# ==========================================
class HtmlDocBuilder(AbstractDocBuilder):
def __init__(self):
self.tags = [] # ここにHTMLタグを書き溜めていく(手を汚す場所)
def add_title(self, text):
self.tags.append(f"{text}\n")
def add_paragraph(self, text):
self.tags.append(f"{text}\n")
def add_bullet_point(self, item):
self.tags.append(f"{item} \n")
def add_formula(self, expr):
self.tags.append(f"${expr}$\n")
def get_result(self):
# タグをすべて結合して、一つのHTMLドキュメントとして完成させる
return "\n\n" + "".join(self.tags) + "\n"
print("--- 1. 【テキスト職人】を配属して構築する ---")
text_worker = TextDocBuilder() # 現場のテキスト職人
director_T = DocumentDirector(builder = text_worker)
director_T.construct_page() # 編集長が原稿を読み上げる
# 最後に職人から完成品を受け取る
text_result = text_worker.get_result()
print(text_result)
print("--- 2. 【HTML職人】へ一発チェンジして構築する ---")
html_worker = HtmlDocBuilder() # 現場のHTML職人
director_H = DocumentDirector(builder = html_worker) # 編集長はそのまま
director_H.construct_page() # まったく同じ原稿をもう一度読み上げる
# 最後に職人から完成品を受け取る
html_result = html_worker.get_result()
print(html_result)
[OUT]
--- 1. 【テキスト職人】を配属して構築する ---
【★ デザインパターンを学ぼう ★】
二元論で語ることは簡単だ。
・ 下請け工場パタン
・ コピー屋さんパタン
[式: E = mc^2]
--- 2. 【HTML職人】へ一発チェンジして構築する ---
デザインパターンを学ぼう
二元論で語ることは簡単だ。
下請け工場パタン
コピー屋さんパタン
$E = mc^2$
3.振り返り
文書コンバータパタンはどうでしたか。
ビルドさんは構成要素の抽象契約者でしかなく、構築はユーザの作ったサブクラスがやってました。
そして、文書の内容はディレクターが決めてました。
だから、手を汚しているのはユーザの方ですね。サブクラスまで作っているし。
それに対して、抽象契約の数式作成はどうだったでしょうか。
フレームワーク社の画面工作員は楽ちんとはいいながらも画面に要素を追加する作業をしてました。
そう考えると、文書コンバータパタンは究極の省エネパタンですね。
このような違いはあるものの、共通点としてはサブクラスの効果です。
抽象クラスは、ルールの明確化と情報の安定的な流れを保証する。
サブクラスに詳細をゆだねることで、抽象クラス自体は不変性が高まるし、変更はサブクラスで柔軟にできる。
私一人でなんでも解決します。わが社がすべて相談に乗ります。なんでも解決します。
ではなく、やることを明確に限定して、変わらないことを価値とする。その代わり変わる部分と連携して、一体となって問題を解決する。
硬と軟を分離してから結合する。異種に分解しておいて合成・連携して動かす。
これって、ベクトルを基底と係数にわけた線形結合で表す、多変数関数・波動ベクトルを偏微分や射影で取り出し、全体として合成することも
わすれないという数学思考と同じ思考回路だなと感じますね。
デザインパターン思考は数学思考だ。
課題:文書コンバータをgeogabraで感じとるにどうしたらよいですか。
ディレクターの入れる文書内容を2つ用意します。
title = "第3章:構築パタン"
p1 = "1つの手順で、多様な表現を作る。"
a = Checkbox() # これの設定で、見出しを「ふつうのテキスト」などにしましょう。
次に表示コンバータの中身を決めます。Textの第1引数はオブジェクト、第2は表示座標です。
TextTitle = If(a, Text(title, (1, 5)), Text("◆ " + title, (1, 5)))
TextBody = If(a, Text(p1, (1, 4)), Text(" ・ " + p1, (1, 3)))
geogebraのTextは文字列+オブジェクト名のように編集表示ができるし、
第2引数で表示位置を決められます。
ということは、パワーポイントのように動的なメッセージ画面も作れるということですね。
ただし、背景の設定を選んで格子線や座標軸を消すのをお忘れなく。
ビューの設定で背景色まで変えられますので、好きな色にカスタマイズするのもいいね。