GoFデザインパターンの生成パターンを会得しているかどうかを示す、な話 構成に関するパターン篇

あいさつ

おはようございます(記事を書いているのは午前10時)。
前回の記事では、GoFデザインパターンのうち、生成に関するパターンを記憶、理解を深めるものでした。

今回は、その続編で「構造に関するパターン」について記憶、自分の言葉で伝えて教養として自分のものにしていきます。どういう全体像を掴んでいるのかを読者のみなさんがイメージできるように配慮いたします。

当方の中では、丁度実例がポンポン浮かんできたので、それを明記するようにしていきたいと思います。

1.Adapter

すでにある機能・APIを使おうとしたけど、設計しているインタフェースではそのまま使えないため、使えるようにインタフェースを整えるガワを作るパターン。SOLIDのインタフェース分離の原則や依存性交換の原則(抽象的ではないので、アプローチとして…)を叶えやすくなります。

例えば、鯖を料理する処理があるとします。それぞれ既存の「鯖クラス」「料理クラス」があるとして、料理(オブジェクト)で鯖(オブジェクト)の「骨を取り除く」メソッドを呼び出そうと考えています。ところが、鯖で必要な「毛抜」オブジェクトが料理にはありません。そこで、新規で「鯖の下ごしらえ」というAdapterクラスを作り、そこで毛抜オブジェクトを準備すれば解決します(もちろん、料理から呼び出すのは「鯖の下ごしらえ」オブジェクトとメソッドです)。

データベースのアクセスに使われるORM(オブジェクト関連マッピング)がAdapterの恩恵を受けられます。

考え方によっては、OSが提供しているAPIやGPUが用意しているドライバも一種のAdapterパターン適応例と考えられます(エンジニアはメーカーやアーキテクチャの差を意識すること無くプログラムを実装できます)。

2.Bridge

似たような構成でクラスの構造が複雑になるのを防ぐため、属性的なものは別のクラスとして準備して橋渡しできるように内包させるパターンです。SOLIDのOpen-Closedの原則、インタフェース分離の原則を叶えやすくなります。

家具クラスという基底クラスがあり、それぞれ「机」「椅子」「台」…といった構成になっている場合、「木の机」クラスとか「鉄の台」クラスを用意するより、家具クラスに「材質」クラスのオブジェクトを抱合しておいたほうが構造としてスッキリします。

実例では、UnityのGameObjectとComponentの関係がそのものズバリなBridgeパターンです。

3.Composite

オブジェクトの集合を設計するとき、オブジェクトやそのサブクラスのものだけではなくい、オブジェクトを組み込める「入れ物」も組み込めて、再帰的な構造を取れるパターンです。

実例では、RubyのArrayやPythonのlistを思い浮かべるとわかりやすいと思います。

4.Decolator

基本的な機能は小さくまとめておいて、あとで機能を追加していけるように、その機能追加の構成も自由に付け替えられればモアベター。

機能ではないけど、(横浜家系や二郎系)ラーメンオブジェクトがあるとすると(もちろん、丼クラス、麺クラス、スープクラス、ダシクラスに分かれて想定してもOK)、そこにチャーシュークラスを組めばチャーシューメンクラスに、もやしクラスを組めばもやしラーメンクラスに、更に大盛り、マシ、マシマシ、マックス盛りクラスを追加すればチャーシューメン大盛りもやしマシマシクラスを設計上可能にする、といったイメージを掴めることで全体像をイメージしやすくなるかと。

個人的な話ですが、一番構造を捉えるのにてこずりました…。最初、.NETのLINQなどかなぁと漠然と思っていたのですが全然違うとわかりましたし…Decolatorの実例がございましたらご教授ご鞭撻よろしくお願い申し上げます。

5.Facade

複雑な構造の設計をする際、様々なインタフェースを個別に用意するのではなく、シンプルにまとめる形式。専用のクラスを設計してもいい。

つかみやすいイメージとしては工房の役割分担。工房オブジェクトのOrderメソッドだけを使っているように見えて、実際は受付オブジェクト、材料選定オブジェクト、加工オブジェクト、組み立てオブジェクト、テストオブジェクト、工程管理オブジェクトがそれぞれ機能しているという図式。

RailsのScaffoldのように、様々な機能が組み込まれて一つの雛形が出来上がるさまはまさしくFacadeの典型例。

6.Flyweight

都度都度大きなコストのかかるオブジェクトを都度都度生成せず、つねに再利用できるように管理しておく方法。

小さいオブジェクトが必要なときには、一度に必要な最大数とオブジェクトの利用時間(生活時間)を把握して、そこからオブジェクトの管理数を算出してオブジェクトプールを作っておけば無駄なメモリが必要なくなるし、GCがある処理系では無駄なGCが必要なくなる。

7.Proxy              

使用するオブジェクトの生成に時間がかかるなど、必要だけどすぐに生成したくない部分を設計する場合、とりあえずは外側に軽いオブジェクトを作って、必要なときに重いオブジェクトを生成する方式。
内部に遅延処理を組み込むよりも実装しやすいのがメリット。生成コストの高い既存オブジェクトを使わざるを得ない場合や、キャッシュを組み込みたい場合も利用できる。

おわりに

今回は、構造に関するパターンを列挙してみました。
できる限り実例を、と思いましたが、DecolatorとProxyが思い浮かばず、不勉強でした。なかなか世の中うまいこと行きません。

次回は、「振る舞いに関するパターン」でやってみたいと思います。数が11もあるので前後半にわけるかもしれませんがぜひともお付き合いを。

この記事が参加している募集

最近の学び

この記事が気に入ったらサポートをしてみませんか?