up:: Programming
オブジェクト指向プログラミングにおける、再利用性の高いコーディングのパターン、いわゆるデザインパターンをまとめた4人のプログラマ。
ギャング・オブ・フォー(GoF) – プログラミング用語解説|Unity高校&ゲームスクールのG学院
デザインパターン (ソフトウェア) - Wikipedia
3分でわかるデザインパターン入門(GoF) - Qiita
【Gang of Four】デザインパターン学習 - Qiita
QianMo / Unity-Design-Pattern:Unity C#で記述された4つのデザインパターンのすべてのギャングと多くの例。UnityC#で記述されたいくつかのゲームプログラミングパターン。|さまざまなデザインパターンのUnity3D C#バージョンの実装
注意
- デザインパターンは銀の弾丸ではない
- デザインパターンは問題に対処するものであり、問題を見つけるものではない
- 必ずデザインパターンに沿う必要はない
- ちゃんと理由があるなら別に破ってもいい
[保存版]人間が読んで理解できるデザインパターン解説1: 作成系(翻訳)|TechRacho by BPS株式会社
作成系
あるクラスをC、そのクラスのインターフェースをCI、そのクラスのファクトリをF、ファクトリのインターフェースをFI。
子を1や2、同じインターフェースを継承した別クラスをiをつけて表す。
そもそも別のクラスの場合は初めに’をつける。
Simple Factory
インスタンスを作るクラスを別に作る。直接インスタンスではなく、インターフェースを返し抽象的にしたほうがいろんなクラスが作れて楽。
なんか他には存在しない。
あるクラスをC、そのクラスのインターフェースをCI、そのクラスのファクトリをFとする。
FがCを作るが返すのはCI。
インスタンス作成時に処理を追加したいとき。
Factory Method
Simple Factoryを子クラスに移譲する手段を作る。
abstractを使って子クラスにSimple Factoryを作らせるイメージ。
ファクトリの子をF1,F2…とする。
Fの子であるF1やF2にファクトリを(内部を指定せず)継承する。
実行時に使う子クラスを動的に決定する必要はあるが、何が必要かは子クラスが決める(クライアントが正確には知らない、知る必要が無い)とき。
Abstract Factory
Factoryを作るFactoryを作る。
その子Factoryの具体的なクラスは指定せず、インターフェースを介して抽象的にする。
ファクトリのインターフェースをFIとする。同じインターフェースを継承した別クラスをiをつけて表す。そもそも別のクラスの場合は初めに’をつける。ファクトリを纏めるファクトリはFbとする。
FIを継承したFiやFiiを作り生成時の処理を書く。
同じように’FIを継承した’Fiや’Fiiにも生成時の処理を書く。
今作ったこのファクトリこそがAbstract Factory。
これでそれぞれのファクトリが出来たので、大きなファクトリFbにまとめられる。
この時、直接Fは指定せずFIや’FIを指定して纏める。
こうすることでクライアントが好きなFactoryを選んで指定できるようになる。
依存が双方向にあったり、作成時の処理が複雑な際に有効。
Builder
Factoryにいろんな変数の初期化が必要な際、いちいちコンストラクタに一つずつ指定していたらめんどくさい。(telescoping constructor)
というわけで、プリセットを用意する。
もちろんプリセットだけでは指定しきれない場合はコンストラクタを用いる。種類ごとに大きさが決まってるピザとか不便極まりない。
Prototype
作ったインスタンスをちょっとだけ変えたクローンが作れるようにする。
既存オブジェクトに近い物や、オブジェクト作成コストがクローン作成を上回るときに。
Singleton
一つのクラスにつき、インスタンスを一つだけであることを保証する。
これに関しては使いすぎるとややこしかったり見にくかったりむしろ効率(開発と稼働の両方)が落ちたりするめんどいやつ。
コンストラクタをprivate、クローン無効、継承不可にする。
どこからアクセスしても同じデータを共有できる。もちろん、どこかで編集したら編集したデータが渡される。まあインスタンス一個なら当然。
以下詳細。
作り方は、private:staticなインスタンスと、public:staticでgetterだけを持ち、戻り値にprivate:staticを渡してくるプロパティを作る。どちらも自分のいるクラス名で定義する。
じゃあそのインスタンスとプロパティってどう作るんだという話だが、使い方が違うだけで普通の変数宣言で定義すればいい。
ついでに共有したいデータをpublicで同じクラスに置いておく。
こうすることで、任意型.インスタンス名でどこからでも取得でき、.変数名で中身のデータを取り出すことが出来る。インスタンスが一つしかないので、データが同じであることは保証される。
これにプロパティのget-onlyを組み合わせると、コンストラクタでしかset出来なくなるので便利。
なお、Unityでこれを使うなら、Awake()内で始まるたびに、インスタンスが2つ以上あったら2つ目以降を破棄する処理を書いておくとより安全。
DontDestroyOnLoad()も忘れずに。
Singletonっていう凄いやつを知った(Unity、C#)|2357|note
呼び出すクラスが決まっていて、そのクラス内でシングルトンにするクラスの要素を必ず使うのなら、呼び出すクラスのコンストラクタ引数にシングルトン予定クラスをセットするだけで済むこともある。とにかく、必要無ければ使わないこと。
構造系
Adapter
ある追加物が既存のインターフェースに適うよう、ラッパークラスを作る。
あるクラスCとインターフェースCI、追加された’Cとインターフェース’CIがすでにあって変更不可とする。クライアントは’CをCと同じくCIを呼ぶだけで動かしたい。
そんな時はCIを継承するラッパークラスWを作る。 Wの中身では ‘Cをプライベート変数に入れるコンストラクタ、’Cの持つメソッドの中で、CIのメソッドとしても問題とないものを実行するCIメソッドが入っている。もちろん、後者は’Cのメソッドを無理に使う必要は無く、W内で独自に実装してもいい。
なお、クライアントは’CとしてWを使うことになる。
Wに互換システムを纏めることで、現在うまく動く既存コードCも’CもいじることなくCIのみで動かせる。仮に修正が入ったら修正するのはWのみ。
ただ、CIで呼び出せるものが増えるのでCIの意味が広くなる。 混乱のもとになる予感しかしない。始めからCが増えることを予想していれば、Cに別のインターフェースC’Iをくっつけ、‘Cが増える際にもC’Iをくっつけることで両方C’Iで呼べるようにできた。このほうがすっきりしてるじゃん。
ともかく設計がしっかりしてたらこれを使うこともなさそうだが、まあ、入れたいものって増えるよね。
というわけなのか、Riderでは’CにCIをつけ、‘CのCIメソッドの中身はWのメソッドから呼ぶ、というラッパークラスの作り方が補完される。
Bridge
機能と内部実装を分ける。
行動を起こすトリガーだけ上位層に設定し、その中身のアクションを下位層に定義するということ。API公開みたいな。
……ということしか書いてないので、下のはあくまで一例。
ある抽象クラスACに対し、機能ごとに具象クラスCCを作っているとする。
このとき抽象クラスの別バージョンとしてACを継承し’ACを作ると、具象クラスもCC継承で’CCを作らなければいけない。これではCCクラスが増えるたびに作るべき’CCクラスも増えるため、‘ACクラスを作るのが困難になる。
そこで抽象クラスの抽象クラス、AACを作る。ここにACが持つCC、つまり機能をまとめておく。
そして他のACや’ACではAACを継承する。こうすることで”ACなどが増えたときに自動的に”CCも呼べる状態になる。
機能をまとめる段階ではインターフェースを活用すると尚いい。
また、具象クラスを放り込むとそのインターフェースを読み取って同じ操作ができるクラス、なんかもこれに当たるらしい。これもうインターフェースの設計思想まんまでは?
Composite
別々のオブジェクトを統一的な方法で操作する。
別々のクラスCA、CBを考える。これらが同じインターフェースCIを継承していれば、CIを通してさらに別のクラスCPがCA、CBを同一に扱い操作することができる。
ディレクトリ操作とかで大事なやつ。よく会社と社員の例えで説明される。
Decorator
クラスに責任を追加する。
ここで言う責任とは、中身のない機能やアクセスできない実装だけではなくその両方。そのクラスが出来るべきこと、何を起こしてどんな状態にできるかということを定義したもの。
要はクラスにアドオンを追加できるようにするということ。
あるクラスCとそのインターフェースICを考える。そしてICを継承しつつ、ICをコンストラクタに追加した(ICがある時のみ作成可能な)クラスCiを作る。Ci内にはIC定義のメソッドオーバーライド(Cのメソッド呼び出しによる同一操作がメイン)と、場合によっては追加される機能を書く。
こうすることで、Cからインスタンスを作成したあと、Ciに放り込むことで機能追加することができる。元の扱いをしたいならICを通せばいいし、追加された機能を使いたいならCiでアクセスすればいい。
Facade
複雑な機能を持つクラスに対して、シンプルな操作系を用意する。
Builderが変数プリセットだったのに対し、これは関数のを用意する感じ。PCをつけるのに回路の知識はいらない、電源一つを押せばいい。
あるクラスCと、Cをコンストラクタに受けつつ、「C内部のメソッドをある目的単位でまとめて呼び出せる関数」を実装したファサードクラスFCを用意。
使うときはC作ってFCに突っ込んでFC経由で触ればいい。
Flyweight
何度も呼び出す必要のあるものはキャッシュする。
ファクトリクラスF、Fをコンストラクタに配置したクラスCFを用意。F内にはキャッシュ用配列変数を置く。
CFからFにクラス生成要請が来たら、まずキャッシュを確認。なければ生成しキャッシュにそのクラスを配置。戻り値は常にキャッシュから呼び出す。
作成例での配列形式はオーダーをキーにした配列。
キャッシュ場所の関係上ファクトリはシングルトンが望ましそうだが、そこはCFのコンストラクタにFを置く事で常に同じFのインスタンスを使うようにしている。
Proxy
別のクラスからあるクラスの機能を呼び出す。
作成例では画像表示前に1000枚読み込む元クラスから、表示関数に手を加えて表示前に一枚分の読み込みを始めるProxyクラスを作成している。
Adapter、Facade、Decoratorと何が違うのかというと、目的の差。Adapterはインターフェースのラップ、Facadeはインターフェースの簡素化、Decoratorは機能追加、Proxyは別の場所からの呼び出しかつ正規呼び出しとの同一視。
Adapter, Facade, Proxy パターンの違いのメモ | Futurismo
Laravel開発で使用されるデザインパターン - Qiita
なのでtechrachoの作成例はどっちかって言うとDecorator。