読者です 読者をやめる 読者になる 読者になる

ゆくゆくは有へと

おかゆ/オカ∃/大鹿有生/彼ノ∅有生 の雑記

「実践Python3」の Template method パターンが

なんかこれで確信したけど、散々過去に悩んだコレも「実践Python3」の捉え方が変なんだろうってことにしておく:

iuk.hateblo.jp

さておき

Template method パターンと Factory method パターンの語の(Pythonにおける)意味がやっと分かってきたからメモ。

この2つは名前の通り、そういうメソッドを作っておこう!っていうパターンで、それぞれのメソッドが「テンプレート的」か「ファクトリー的」かってことですね。

Template method は そのクラスがもつ他の抽象メソッド を使って書かれた、より大きな操作をするメソッド。これは ABCで実装されて、その具象クラスは抽象メソッドを実装すると同時に、この template method も使えるようになるって寸法よ!素敵! Template methodパターンは、ABCじゃなくて基底クラスを使ってもいい。その場合は、template method 内で使われる他のメソッドというのが抽象メソッドではなくてデフォルト実装されたメソッドになる。

一方、Factory method は、ABC では抽象メソッドとして定義されるメソッド。で、具象クラスにおいてファクトリーとして働く、つまり、特定のインターフェースを備えたオブジェクトを吐き出すメソッドとして実装される。

「なんで特定のインターフェイスを備えたやつじゃないといかんのか?」ということについては、他のところでそういうインターフェース持ってる前提で色々書いちゃうからでしょうな。

ということを考えると、Template method で使う 抽象メソッド の中には大雑把には Factory method も含まれる。ただし厳密にいえば、それは抽象メソッドではなくて抽象オブジェクト(具体的にそれがどんなものか決まってない(インターフェースしか分からん)という意味での抽象)なので、ちゃんと区別するに値はするのかな?という感じ。

生成されるオブジェクトのインターフェースに従ってあーだこーだするという点で template method パターンよりも手の込んだ感じしますよね。そういう感じよ(?)

使用する抽象的なのが自身のクラス内のメソッドで完結するのが template method パターン、インターフェースの要請だけしておいて、外部からオブジェクトもらってこようとするのが Factory method パターンかなと理解した。

ただ、この書き方だと、「Template method パターンにさらに Factory method パターンを適用したもの」という側面でしか Factory method パターンを描写できてない気がする。

でも、「Template Methodパターンをオブジェクト生成の場面に適応させたデザインパターンと言えます」*1 らしいので、これでいいのかもしれない。

いずれにせよ、Template/Factory method パターンどちらも、「上位クラスが下位クラスに部品(メソッド)の実装丸投げするパターン」ってことだろう。

そういう意味で、このパターンはクラスとクラスのパターン(クラスパターン)らしい*2

他のパターンとのつながり

ちょっと変えたら別のパターンになるってのが結構あるっぽい。

Strategy パターン

一定の処理とか関数をアウトソーシング化してしまおうぜっていうパターン。Template/Factory method パターンでは、サブクラスの定義で自由度がなくなってしまう(メソッドとかが一意に定まる)けど、Strategy パターンは処理・関数を引数化してしまうことで自由度をあげられる。

あと、クラスとクラスのパターンから、オブジェクトとオブジェクトのパターン(だよね?)に持ち込めるので、ABCとか使わなくていいという意味では楽。

Factory method パターンにおけるサブクラスの実装バリエーションが増えてきたら Strategy パターンに移行すればいいんじゃないかな。たとえば Factory method を3種定義するABCを作るとして、それぞれのメソッドの実装(どのクラスのインスタンスをつくるか)の通りが5つあるとしたら、完全に網羅するには 53 = 125 ものサブクラスが必要になるわけで、そうなると早々に引数化してしまったほうがいい。

もちろん引数化するというのは無用な柔軟さを与えてしまうことにもなるので、そこのところはトレードオフでしょうね。

Bridge パターン

UML図的には Strategy パターンと同じらしい*3

Strategy パターンの UML クラス図は Bridge パターンのものと同じである。しかし、これら二つのデザインパターンはその意図が同じではない。Strategy パターンは 振る舞い に対するパターンであるが、Bridge パターンは 構造 に対するパターンである。

こういう、絵面的には同じだけど意図が違うってパターン割とあるっぽいよね。

Strategy パターンは「振る舞いのアウトソーシング」で、Bridge パターンは割と単純に「モノの分離」。

抽象メソッドの実装という形ではなくて、欲しいモノは引数としてもらおうというパターン。

「xを食べる男」抽象クラスを具象化して「リンゴを食べる男」「バナナを食べる男」クラスを作るんじゃなくて、「xを食べる男」クラスを作っておいて、そのインスタンスの引数に食べ物をとって「リンゴを食べる男」「バナナを食べる男」インスタンス作ろうって話(たぶん)。

いざ「xを食べる男」クラスのサブクラスを作ろうとしたときに対応しやすいのが動機。

Abstract Factory パターン

Factory Method と Abstract Factory の違いを順に理解する | Futurismo より:

Abstract Factory は Factory Method のカプセル化に過ぎないことを示す.

構造的な側面だけに着目すれば、たしかにカプセル化にすぎないかなと思うけど、デザインパターンは構造的な側面だけに注目して理解するものでもなさそうだしね(Strategy/Bridgeパターン然り)。

Factory method パターンでは、その抽象なファクトリーメソッドを使って、より大きい処理をしようとするのはそのクラス自身(つまりそういうメソッドをもつ)。一方、Abstract Factory パターンでは、そういう大きい処理をしようとするのはクラスではなくて外部の誰か。そういう意味で、Abstract Factoryパターンはオブジェクトパターン(らしい)。

ファクトリー関数の利点は、共通(固定した)名前で(所定の)インスタンス作れることなわけだから、誰かに使ってもらわないと(しかも、どのインスタンスがきてもいいようなある程度の普遍性をもった処理の中で)あまり生かされているとは言えない。

Factory method パターンは最終的に自分の中に書いた処理のためのパターンで、Abstract Factory パターンは外部の処理のためのパターンだね(繰り返し)。

とはいえ、その意図を除けば、一連の整合性のあるファクトリー関数(メソッド)群をまとめてしまおうというのが Abstract Factory パターン。

結局、ふたをあければファクトリー関数がそこにあるわけで、共通のインターフェースで異なるファクトリー群が使えるというのが Abstract Factory パターンの美味しささね。

とはいえ、「実践Python3」で Template method パターン実演なのに、template method もってない抽象クラスもってきたりするのは草生えたけど、まあ確かに「何もしない」ことも、共通処理の1つに入ると言われれば…首肯せざるをえない。

そういう意味では、抽象クラスにfactory method が使われている共通処理のないようなものもまた factory method パターンに則ってると言える。そう考えると、factory method をカプセル化したものが Abstract factory だ、というのはあながち間違いでもないのかもしれない。