2009年12月7日月曜日

Scala / multiple classification

multiple classification (多重分類/多重型付け)について、Scala ベースで考えてみた。 Analysis Patterns」 から抜粋した上のクラス図(Odell表記)の意味は、
  • 顧客のインスタンスは、サブクラスである個人顧客または法人顧客のいずれかのインスタンスである。
  • 顧客のインスタンスは、サブクラスである重要顧客インスタンスである場合もあり、そうでない場合もある。
上図の分析モデルでは2つの系統の重なりを禁じていないので、ベン図で書くとこんな風に重なりが生じる。 こういうのを、オブジェクト指向分析の用語で multiple classification と言う。生活世界の感覚では、別段、不自然な事ではないけど、従来の single-classification ベースの言語で考えると、ちょっと面倒くさくなる。 つまり重なった部分についても、重要個人顧客、重要法人顧客といったように、組み合わせの分だけクラスを定義する事になる。これを避けて、種別を示す列挙定数を持たせるなどするけど、結局、分析と実装のギャップが広がってしまう。 で、前に Scala を少し調べたとき、分析と実装を乖離させる事なく、上述のような複数系統の型付けを自然に実装できそうな感じがしたので、ちょっと簡単なコードで確かめてみたい。 以下のようななんちゃって仕様を仮定する。
  • 顧客(Customer)は値引き率をあらわすメソッド discount() を持つ。
  • サブクラス 個人顧客(PersonalCustomer)の discount()は、2月生まれの人を2割引する
  • サブクラス 法人顧客(Corporation)の discount()は、従業員10人未満の法人を1割引する。
  • サブクラス 重要顧客の discount()は、個人/法人の discount() で値引きされた率に加えて、さらに3割引する。例えば 2月生まれの重要個人顧客は 2割引の 3割引で 4.4割引になる(1-(1-0.2)*(1-0.3)=0.44)。
こんなコードを書いてみた。
class Customer {
 def discount: Double = 0
}
class PersonalCustomer(val birthMonth: Int) extends Customer {
 override def discount: Double = if (2 == birthMonth) 2 else 0
}
class Corporation(val employeeNum: Int) extends Customer {
 override def discount: Double = if (10 > employeeNum) 1 else 0
}
trait PriorityCustomer extends Customer {
 override def discount: Double = (100 - (10 - super.discount) * 7) / 10
}
以下、結果。
scala> new PersonalCustomer(2).discount
res62: Double = 2.0

scala> (new PersonalCustomer(2) with PriorityCustomer).discount
res63: Double = 4.4

scala> new Corporation(5).discount
res64: Double = 1.0

scala> (new Corporation(5) with PriorityCustomer).discount
res65: Double = 3.7
とりあえず計算はできたらしい。重なり部分のクラスを個別に定義しなくても、型付けを多重化して仕様を合成できた。ただし、インタプリタでなくソースコードでの生成を考えた場合、組み合わせる型を with に続けてハードコードするのではなく、実行時に与えられるようにしたい。そうでないと、実質、個人重要顧客/法人重要顧客に相当する無名クラスを定義しただけにすぎない感じがする。これじゃ結局、single-classification だ。うーん、もうちょい調べないと・・・

0 件のコメント:

コメントを投稿