いつか作ります RSSフィード

2008-08-02

OOコード養成ギプスのへっぽこ解説 19:39 OOコード養成ギプスのへっぽこ解説 - いつか作ります を含むブックマーク はてなブックマーク - OOコード養成ギプスのへっぽこ解説 - いつか作ります OOコード養成ギプスのへっぽこ解説 - いつか作ります のブックマークコメント

そもそも何を言っているのか分からない、というレベルの人間向け。

まあ、そういう自分もどこまで理解してるか怪しいものなのだが、だからこそ書く。

例に出すものの中には明らかに設計がマズいものも混ざってるが、あくまで問題になってる部分だけ見るって事で。

インデントは1メソッド1レベルのみ

ループのネスト、制御構造のネストをすると、その階層分だけ「いまどういう状態か」を覚えながらコードを読む必要が出てくる。

「ケータイで、キャリアがDoCoMoで、ユーザは18歳以上で、すべての所持アイテムに対し…」

じゃなくて。

  • ケータイかどうかを判定したら、ケータイ処理用の関数を呼ぶ。
    • ケータイ処理用の関数ではキャリアを判別だけしてDoCoMoユーザ用の関数を呼ぶ。
      • DoCoMoユーザ用の関数では18歳未満かどうかを判定して、子供向けの関数を呼ぶ。

複数のCSVファイルを扱うときは

  • 単一のファイルを開く
    • 単一の行を開く
      • 単一のセルをパースする

というように、関数を階層構造で分ける。*1

'else'は禁止。条件文はifだけ。

たぶん上記と絡む問題。elseを使うと、条件分岐のネストが可能になる。elseを使わなければ、そもそも条件分岐のネストを完全に封じられる。

あと、多重ネストのif-elseは、どのレベルに対応しているか読みづらいし書き間違えやすい。

追記:ガード節を使うとこのプラクティスをクリアしやすい。

if(...){
 // 処理A
}else{
 // 処理B
}

の場合、

if(){
  return 処理A関数();
}
 // 処理B、もしくは関数呼び出しを即return

とすると、処理Bの部分がちょっとスッキリする。条件分岐の整理にも有効。ファウラーの『リファクタリング』参照。

ガード節の後を関数呼び出しにするかガリガリ書くかは処理しだい。「ある変数がセットされているかを調べる」ガード節なら書いてしまったほうがいいが、「変数でモードを切り替える」ような場合は別のメソッドに切り出した方がいい。

全部のプリミティブとstringをラップせよ。"primitive obsession."だ。

"primitive obsession"は、プリミティブ型の使用をしないという事。要は全部クラス(あるいは構造体)にしろって事。

  • ひとつ。たとえばURL文字列を専用のクラスに格納していると、ドメインだけ抜き出すとかファイルパスだけ変えるとかの操作が可能になる。正しいURL形式かのチェックも可能になる。こんな感じで機能をメソッドと言う形で追加できる。
    • プリミティブ型にはふつうメソッドを持たせられないので、URLを文字列として格納していた場合、こういう機能がコードの各所に散る事になる。セットで使うものは、セットにしておくべし。
  • ふたつ。その数字なり文字列なりが「何を示しているのか」が非常に明確になる。変数名よりもかなり明白で厳密な形で。
  • みっつ。専用型になっているものをプリミティブに直すのは簡単だが、その逆は困難(すべてのintに対し、「これは年齢を示しているのか」を考えないといけない)。

1行に1つのドットのみ使うこと

company.section.team.leader.work() という指示の出し方はよろしくないですよ、と。

company.work()がsection.work()を、section.work()がteam.work()を…と階層的に呼び出されるようにしておかないと、発注元の会社が相手先企業の内部構造に依存する事になる。

Rubyとかでstring.split.sort.reverse.eachとかやる場合は多分例外。階層を辿るアクセスじゃなくて単なる処理のチェインだし。

ただ、メンバーや引数の関数の戻り値に関数を適用し、その戻り値に関数を…という呼び出しが多い場合はクラス設計を見直すべきかも知れない。

名前は省略しない

自動補完が利く環境なら、省略して分かりづらくする意味が無い。利かない環境では原文の通り。

エンティティを小さく保つこと

1メソッド、1クラス、1パッケージが大きすぎると、再利用しにくくなってOOPの利点が活かせない。


3つ以上のインスタンス変数を持ったクラスは使わないこと

要はインスタンス変数が多すぎると機能が増えすぎでよろしくない、という事。「3以上」なのは、理論上考えられる最低値がこれだからだろう。2つをNGにすると組み合わせる事ができない。

よく引き合いに出される、Point3Dクラスのx,y,zのような、本質的に3以上のものが1セットな概念は多分例外だろう。

追記:このパートに限った話ではないが、関数型言語の考え方がかなり色濃く現れている。RubyやPython、JavaScript以外でこれを完全に実践するのは困難。

ファーストクラスコレクションを使うこと

コレクションを含んだクラスが必要なら、そういう風に書くんだ

まんま。たとえば

class Team
{
	string teamName
	array members
}

と書きたくなったら、Members*2というクラスを定義し、それをメンバとして持たせろ、ということ。

配列じゃなくてコレクションクラスを使うのは、プリミティブ型を使うなってのと同じ。


セッター、ゲッター、プロパティを使わない

前のエントリで触れなかった辺りの事を。

あるクラスからメンバをget()して、それを元に処理をする、というコードを書いたら、そこでいったん立ち止まってこう考える。

この処理は、get元のクラスがメソッドとして持つべきではないか?

一般に、メソッドと言うのは、必要な変数を持っているクラスに実装するのが良い。そうじゃないと、あるものに対する処理があちこちに散らばる結果になる。

「聞くな、言え」というのはこの事。そのクラスが知らないものを他のクラスに「聞いて」自分で処理をするよりも、他のクラスのメソッドを直接呼び出しその処理をさせる方が良い。「言うな、聞け」よりも「尋ねるな、命令しろ」の方がニュアンスが近いかもしれない。

で。setterが使えないとなると、メソッドの実行に必要な非メンバー変数は、引数として与える形が多くなっていく。これがDependency Injectionパターンの基礎。

*1:制御構造ではなく、データ構造を変える事でも実現できるが、同じことなので割愛

*2:あんま良くない名前ですね

新着エントリは上に追加。コメントは「はてなユーザのみ」、公開設定はパブリック (だれでも閲覧ができます)。