デザインパターン
Menu
オブジェクト指向
オブジェクト指向プログラミングには、
対象の性質をまとめて記述する それを継承して再利用するという特徴がある。関数型言語には、
入力が決まると出力が決まる 型の整合性という特徴がある。
オブジェクト指向プログラミングでは、記述の方法が様々で、うまく再利用できない形になることも多い。拡張性を得るために、単に継承するだけでなく、元のソースコードを修正した方が良い場合もある。これをリファクタリングと言う。
最初に、もっとも簡単かつ素直な実装を考える
それの必要な拡張の可能性を考える。それを用意にするデザインパターンがあれば、それを拡張が必要になった時に実装する
デザインパターン
オブジェクト指向プログラミングの定番の技術をまとめたもの。特に有用なものとして、
State Pattern Factory Pattern Abstract Factory Pattern Command Patternをここではあげる。
Optional ( Maybe ) Method Chain ( Functor / fmap ) Dependency Injection ( Monad )
交通信号の例
普通の switch 文を使った交通信号 Enum を使って状態を作り、swtich 文で状態毎の動作をする。
State Pattern
状態毎に class を使い、状態を格納する変数で切り替える。State 自体には状態はない。
Class で状態名がわかる。
Factory Pattern
状態の次のState instance を持つようにする。この構成のinstanceを作るために Factory method を用意する。
Abstract Factory Pattern
同じ Interface を持つインスタンスを必要に応じて選択して作成する Factory 。
Command Pattern
動作毎の class のinstanceを作り、queue に入れて、command 列の制御を行う方法。Undo とかに使う
あるいは remote access するコマンド
Exception ではなく Optional / Maybe を使う
Monad を使うということException は実は結構遅い
病的に情報を隠すのはしない
private を default の引数みたいに使用するプログラムの正しさが隠された情報に依存するのはだめ
Dependency Injection
classの依存性(特に transaction)を class 自体にもたせない自分のお勧めは
method の引数に、依存性をまとめたオブジェクト(コンテナ)を常に持たせる
dependency object を private な field に入れるのは僕は間違いだと思う
method chain
a.method_of_a(x).metho_of_some(y) ;みたいに呼び出す。fmap。エラー処理はどうする? ( Optional )
Anti Pattern
やってはいけないこと。ここでは、
デザインパターンを目標にコードを書く 大域変数 Singleton 1,2… 動きません コードのコピペ 継承したクラス間の強い依存性 継承したクラス同士での同じ名前のprivate変数 ad-hoc な port の利用をあげる。
デザインパターンを目標にコードを書かない
if 文 switch 文ですむなら、無理にデザインパターンを使わない方が良い。デザインパターンは複雑なコードに対するcompromise であり、それを目標して、コードを歪めない。
Singleton / 大域変数を使わない
Singleton と大域変数は基本的に不要。(log 以外はすべて除去)大域変数は、基本的に必要ない。例外は、stdout や log などだけ。大域変数があったら、
なぜ、その変数は大域でなければならないのか説明する説明できないなら除去する。
理由 並列処理でめんどう ( Thread Local も trouble ことが多い) プログラム全体の依存度をあげてしまう
Read Only なら
局所変数の再代入をしない
局所変数を使いまわさない。インスタンスの状態以外は局所変数ですます。値を変えたら新しい変数に代入する。
オブジェクトの状態を表す変数は、method により一回だけ変更する。
オブジェクトの private変数を使わない
継承したクラス同士での同じ名前があると複数作られる。オブジェクトの状態は setter/getter でアクセスできるようにする。
同じ名前の複数の変数を一つのインスタンスの中で使わない。
初期化と宣言を別にしない
初期化されない変数がないようにする。必要なところで宣言する。基本的に定数 / Readonly で使う。値を変更したければ新しく変数を作る。
i = i + 1 ではなくて、 j = i + 1 とする
「1,2… 動きません」にしない
複数のインスタンスがあっても動作するようにする。
コードの拡張を予測し、それを阻害しないコードにしておく。
大域変数/Singletonが「1,2… 動きません」の原因であることが多い。
コードのコピペをしない
コピペして少し変更したコードを、後で変更したらどうなるかを考える。コピペは基本的に悪。
同じことを二箇所に書かない
ad-hoc な port の利用をしない
TCP/IP の port は動的に確保する。その port が空いてない場合を意識してプログラムする。
interface / implementtion
に機能を付け加える時は、両方を変更する必要がある階層をまたぐと、それ全部を変更する必要がある
機能を追加する時に
interfaceの実装を増やす
みたいにすると避けられる
引数の異なる同じな前の method が大量にあり、動作が異なる
default とかで避けようとするそうではなくて、引数をまとめて Object にする
それを渡す
そうすると引数は一つですむ