はじめに
変更しやすいコードが書けないのにソフトウェア開発とか舐めているのですか
2024年の干支の可能性がある天使様1、ごめんなさい〜、nikkieです。
「かわいい」と技術書が夢の合体を果たした、ちょうぜつ本(『ちょうぜつソフトウェア設計入門』)!🤗
As known as 技術書界のきらら、アニメ企画よ来い!
昨年から読書会を共同主催しており、現在は第8章「デザインパターン」を読み進めています。
読んだ範囲から、Decoratorを取り上げます。
目次
前回のちょうぜつ本!
前から順に読み進めて第8章に突入し、読書会では範囲を細かくしてじっくりと読み進めています。
- 読書ログ | #ちょうぜつ本 第8章 Template Methodパターン 〜穴埋め問題にする〜 - nikkie-ftnextの日記
- 読書&写経ログ | #ちょうぜつ本 第8章 Bridgeパターン 〜has-a関係でとらえて、複数系統の組合せを表現できる!〜 - nikkie-ftnextの日記
- 読書ログ | #ちょうぜつ本 (第8章)でも #fukabori (48)でも、ただしSingleton、テメーはダメだ - nikkie-ftnextの日記
- 読書ログ | #ちょうぜつ本 第8章 Facadeパターン 〜表玄関だけを通って!〜 - nikkie-ftnextの日記
- 読書&写経ログ | #ちょうぜつ本 第8章 Mediatorパターン 〜ColleagueたちにMediator自身を渡して、必ずMediatorを介在させる!〜 - nikkie-ftnextの日記
Python文法のデコレータはProxyパターンでした
Decoratorパターン
呼び出し元と呼び出し先のオブジェクトの間に割って入り、呼び出し先オブジェクトをラップ(包み)ます。
その目的は、新たなメソッド追加!
Decoratorパターンは、既存メソッドの振る舞いはそのままにし、新たなメソッドを追加する拡張ラッパーです。(Kindle版 p.380)
図形描画の例
Pythonで写経したコードはこちら
線分が引けるインターフェースDrawingInterface
があります。
メソッド:
- start_at
- line_to
- start_atで指定した点からline_toに渡した点まで線分を引き、start_atを更新
これは小さくて私好みです(小さいは、正義🤗)。
組合せられるシンプルさも感じますね。
ですが、使い勝手はそこまでよくありません。
四角形や三角形を描こうとなったら、何回もline_toを呼ぶことになります。
そこで、Decoratorパターンで便利な描画メソッドを追加します。
DrawingInterface
を実装したDecorativeDrawing
クラス爆誕!
DrawingInterface
を実装したクラスのインスタンスを持つ実装(☆)DecorativeDrawing
自体はDrawingInterface
を実装するので以下の2つのメソッドは持つ- start_at
- line_to
- これらは
DecorativeDrawing
が持つインスタンス(☆)に委譲して実装
- これらは
- Decoratorパターンによる便利な描画メソッド
- rectangle
- triangle
- line_toの呼び出しをまとめて実装
「正しい使い方が簡単に」なった!
Decoratorパターンは継承と何が違うのか?
Decoratorパターンの方がはるかに柔軟 (Kindle版 p.383)
この言葉に撃ち抜かれました。
受け取り方は「変更しやすいコードが書けないのにソフトウェア開発とか舐めているのですか」と同じです。
これまで食らったわからん殺し、あれはDecoratorパターンを採用すべきだったけど継承でやっちゃったからか〜(あちゃ〜 ><)
書籍に書いてあるとおりなんですが、
DrawingInterface
を実装したクラスがA,Bと複数あっても、委譲しているからDecorativeDrawing
1つで済むんです!DecorativeDrawing
はDrawingInterface
なんですよDrawingInterface
を使っているところではDecorativeDrawing
に置き換えても動きます!(追加した便利なメソッドは使われないですが)
Proxyとの違いまとめ
自分の言葉でまとめると
- Proxyは既存のメソッドの振る舞いを拡張
- nikkieはPython文法のデコレータを念頭に置いている
- 引数の数や型、返り値の型は変わらない
- 既存のメソッド呼び出しの前後に処理を追加できる(これがProxyによる拡張)
- ただしProxyの本質は代理を立ててサボる
- 今回の例だとProxyはstart_atやline_toを拡張する
- Decoratorは新規のメソッドを追加
- 既存のメソッドの振る舞いは変えない
- 既存のメソッドをまとめた新規メソッドを追加(
DecorativeDrawing
の例)
終わりに
ちょうぜつ本 8章よりDecoratorパターンでした。
知らなかったばっかりに過去に継承を選択して、やっちまってましたね(ダメージを負いました3)
読書会ではDecorator(装飾)、Proxy(代理)という語の意味ででとらえてはという知見も知れました。
ありがとうございます!
P.S. Decoratorを読んでいて浮かんだ例
厳密には当てはまらないかもしれないですが、こちら
ByteLevelBPETokenizerはTokenizerインスタンスを持つんですよね。
で、ByteLevelBPETokenizerのtrainメソッドは、Tokenizerのtrainメソッドを呼び出します(委譲)。
https://github.com/huggingface/tokenizers/blob/v0.15.0/bindings/python/py_src/tokenizers/implementations/byte_level_bpe.py#L98
新しいメソッドは追加していなさそうなので、Decoratorとは呼べないかもしれませんが、委譲の例として思い出しました。
委譲のよさはちょうぜつ本を読み進める中で分からされている感覚です。
- ちょうぜつ本読書ログシリーズではおなじみのこちらの書き出し。元は『お隣の天使様にいつの間にか駄目人間にされていた件』の「家事ができないのに一人暮らしとか舐めているのですか」です↩
- Bridgeも委譲を使って、クラスの数が増えるのに対処していましたね↩
- かわいい要素がなかったら危なかった...↩