何をクラス分割の指針とすべきか
最近なんとなく:
ようにすれば適切にクラス分割できるんじゃないかと考えていました。
今日「凝集度と結合度:このコードのどこが悪いのか? - ITmedia エンタープライズ」という記事を読み、基本的にこのような考え方で間違いなさそうだと自信が持てました。
LCOM(Lack of Cohesion in Methods)
その記事では、いわゆる凝集度(の欠乏度)の測定手法として、LCOM(Lack of Cohesion in Methods)という計算式が紹介されています。インスタンス変数の数、メソッドの数、そして各インスタンス変数にアクセスするメソッドの数を用いて、計算結果が0に近いほど凝集度が高いクラスとみなす手法です。
記事に掲載されている凝集度の低いクラスの例はこういうものです(LCOMが0.666…)。
class SampleClass { private int a1, a2, a3, a4; public void m1() { a1=0; a2=1; } public void m2() { a1=1; a2=2; } public void m3() { a3=0; a4=3; } public void m4() { a3=1; a4=2; } }
これを凝集度が高くなるようクラス分割するとこうなります(LCOMが0)。
class SampleClassA { private int a1, a2; public void m1() { a1=0; a2=1; } public void m2() { a1=1; a2=2; } } class SampleClassB { private int a3, a4; public void m3() { a3=0; a4=3; } public void m4() { a3=1; a4=2; } }
これは極端な例でしょうが、インスタンス変数が多い場合、一部のインスタンス変数にしかアクセスしないメソッドができやすい傾向はあると思います。
クラス分割の指針
冒頭に挙げた2点を指針っぽく言い換えると:
- すべてのインスタンス変数にアクセスするメソッドのみをクラスに配置せよ
となります。「凝集度を高めよ」という抽象的なスローガンよりよほど分かりやすいと思いますが、どうでしょうか。
この指針、必ず守れるわけではないでしょうが、私はできるだけ従ってみようと考えています。
インスタンス変数のないクラスは?
ちなみにインスタンス変数のないクラスは、ほぼ不要であると個人的には思っています。そのようなクラスは、スタティックメソッドの集まりになるのでしょうが:
Static methods are procedural! In OO language stick to OO. And as far as Math.abs(-5) goes, I think Java got it wrong. I really want to write -5.abs(). Ruby got that one right.
Static Methods are Death to Testability
という Miško Hevery 氏の意見に私は賛成です。
追記(2009-05-31)
(DBのレコード情報などを保持する)DTOのようにタプルであることに意味がある場合はどうなんだろう。「インスタンス変数のないクラス」も、ロジックを束ねて多態したい場合はどうなんだろう。
はてなブックマーク - terazzoのブックマーク / 2009年5月31日
コメントありがとうございます。DTOならば、インスタンス変数ごとのgetterメソッドくらいはないと困りそうですね。
多態については、どうせ何かしらの引数オブジェクトをロジックに渡すのでしょうから、ならばそのロジックを引数オブジェクト自体のメソッドにしてしまえ、という感じです。