nikkie-ftnextの日記

イベントレポートや読書メモを発信

読書ログ | #ちょうぜつ本 第8章 Decoratorパターン 〜新たなメソッドを追加するラッパー。委譲による実装がうまい!〜

はじめに

変更しやすいコードが書けないのにソフトウェア開発とか舐めているのですか

2024年の干支の可能性がある天使様1、ごめんなさい〜、nikkieです。

「かわいい」と技術書が夢の合体を果たした、ちょうぜつ本(『ちょうぜつソフトウェア設計入門』)!🤗
As known as 技術書界のきらら、アニメ企画よ来い!

昨年から読書会を共同主催しており、現在は第8章「デザインパターン」を読み進めています。
読んだ範囲から、Decoratorを取り上げます。

目次

前回のちょうぜつ本!

前から順に読み進めて第8章に突入し、読書会では範囲を細かくしてじっくりと読み進めています。

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
  • Decoratorパターンによる便利な描画メソッド
    • rectangle
    • triangle
      • line_toの呼び出しをまとめて実装

正しい使い方が簡単に」なった!

Decoratorパターンは継承と何が違うのか?

Decoratorパターンの方がはるかに柔軟 (Kindle版 p.383)

この言葉に撃ち抜かれました。
受け取り方は「変更しやすいコードが書けないのにソフトウェア開発とか舐めているのですか」と同じです。
これまで食らったわからん殺し、あれはDecoratorパターンを採用すべきだったけど継承でやっちゃったからか〜(あちゃ〜 ><)

書籍に書いてあるとおりなんですが、

  • DrawingInterfaceを実装したクラスがA,Bと複数あっても、委譲しているからDecorativeDrawing1つで済むんです!
    • AのインスタンスとBのインスタンスそれぞれ渡せばよいですね
    • 継承でやっているとDecorativeDrawingをAのケースで継承、Bのケースで継承と、DrawingInterfaceを実装したクラスごとに増えていっちゃいますね(経験済み)2
  • DecorativeDrawingDrawingInterfaceなんですよ
    • 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とは呼べないかもしれませんが、委譲の例として思い出しました。
委譲のよさはちょうぜつ本を読み進める中で分からされている感覚です。


  1. ちょうぜつ本読書ログシリーズではおなじみのこちらの書き出し。元は『お隣の天使様にいつの間にか駄目人間にされていた件』の「家事ができないのに一人暮らしとか舐めているのですか」です
  2. Bridgeも委譲を使って、クラスの数が増えるのに対処していましたね
  3. かわいい要素がなかったら危なかった...