読者です 読者をやめる 読者になる 読者になる

考える場所

ココロとカラダ、思考する全部

オブジェクト指向について考えてみたが答えらしいものは見つからなかった

お仕事として書いている目の前のコードと本質的ななにか(オブジェクト指向らしさ?)にギャップがあると常に感じてきた。オブジェクト指向とはなんだろうかと。

ドメイン駆動設計という考え方を知って「エリック・エヴァンスのドメイン駆動設計」を読んだ。関連してヘキサゴナルアーキテクチャCQRSのようなアーキテクチャも知った。増田さんかとじゅんさんなどのお名前をよく拝見するのでウォッチしたりもしている。

ここにきて思ったのは、これで完全なオブジェクト指向だとするものはなくて、ましてすべての業務に適用することができるような銀の弾丸は存在しないだろうということ。書いてまた書いて、美しく書くことができたなと思えるコードが結局よいということ。それが単に知識を吐き出すだけのことではないなら、反復練習するしかないだろう。正しいと思うやり方を繰り返す。繰り返して違うと思ったらまた別の正しいを見つける。それが訓練だと思う。

今、正しいと思う3つのやり方

このところ3つの原則が気に入っていて訓練してみている。今回はそれを紹介する。

  • 副作用のないオブジェクト
  • ファーストクラスコレクション
  • 仕様パターン

実際に書いてみるとすごく効果的であると感じるが、それぞれは単純で難しくはない。しかも書いていて気持ちがいいのだ。

副作用のないオブジェクト

インスタンス化したオブジェクトを不変する。つまり変更を加えられないようにする。VO(Value Object)と呼ばれるもの。

たとえば次の数式(プログラミングではない)を考える。

a = 1/2
b = 1/3
c = a + b = 5/6

このa + bにあたってa5/6に変化しないことは自然な理解だと思う。

同じように考えてプログラミングで次のように書く。

a = new Rational(1,2)
b = new Rational(1,3)
c = a + b

(書き方は問題ではない、a.add(b)であっても)ここでもやはりaのインスタンスが5/6にならないようにする。1/2のまま。1/2という値に状態の変化はないのだ。+メソッドはaのインスタンスとbのインスタンスから、結果として新しいインスタンスを生成する。

ファーストクラスコレクション

コレクションのためのオブジェクトを設ける。コレクションを取り扱いたい側に直接Listなどを繰り返して操作させない。操作はコレクションのためのオブジェクトがする。

たとえば男性サンプルの平均値を次のようにして得られるようにする。

samples = repository.findAll
males = samples.filterMale
println(males.ave)

コレクションのためのオブジェクトには次のようなメソッドをもたせる。

class Samples(list: Array[Sample]) {
  def filterMale: Samples = 男性でフィルタした新しいコレクションオブジェクト
  def filterFemale: Samples = 女性でフィルタした新しいコレクションオブジェクト
  def ave: Double = このコレクションが含むサンプルすべての平均値
}

このようにすると、すべてのサンプルの平均値・男性サンプルの平均値・女性サンプルの平均値を得るロジックを実装したことになる。ロジックをパズルのように組み立てて結果のバリエーションを増やすことができる上、ひとつひとつのロジックは小さくてシンプルである。

仕様パターン

仕様をモデル化する。つまり仕様として独立したクラス(オブジェクト)を設ける。

たとえば男性サンプルであることの仕様を次のようにする。

class MaleSampleSpecification {
  def isSatisfiedBy(sample: Sample): Boolean = 戸籍上の性別が男 || 性自認が男
}

Sampleが複雑になり過ぎてしまうことを避けるため、業務ルールや判定などを別に切り出す。クラスは多くなるが、クラスの役割をより明確に分離することができる。

さらに、ファーストクラスコレクションと合わせて次のようにすることを考える。コレクションのためのオブジェクトから仕様に関するロジックを分離し、よりシンプルにすることができる。

samples = repository.findAll
males = samples.filter(new MaleSampleSpecification)
println(males.ave)

プログラミングの洗練は終わりがない

システム化対象のドメインによってはこれらのパターンを原則にして馬鹿正直にやるまでもない場合がある。パフォーマンスの都合などで適用がかなわない場合もでてくる。だからこれらはいつだって正しいというわけではない。こういう場合は適用できてこういう場合は適用できないという、一意な判断基準があるわけでもない。やはり冒頭に述べたように、書いてまた書いてこうかなどうかなと思考錯誤することがオブジェクト指向をやるということなんだろうなと思う。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)