連載第15-2回
UMLの基礎と応用

(株)NTTデータ 技術開発本部 副本部長
山本修一郎


コレクション

[例]
context Order::addArticle (a:Article): void
pre: a.number <> 0
post: ordered_articles = ordered_articles@pre -> including (a)
post: sum = sum@pre + a.price

[説明]
事前条件は操作の引数で指定された商品オブジェクトの個数属性が0個ではないことである。事後条件は引数で指定された商品を注文された商品のコレクションに追加する。また商品の価格priceを合計sumに加算する。

 ここで、主な記号の意味は次のようになる。

ordered_articles : コレクション
including (a):コレクション演算子である。引数で指定されたaを含むコレクションを返す。

表3 コレクション演算子
表3 コレクション演算子

イテレータ

 イテレータを用いることによりコレクション型の要素の属性に関する反復操作を定義することができる。

[構文]イテレータ
<Collection名> -> iterate (i : <iteratorType>;  -- 反復操作の対象を指定
acc : <AccType> = <initial-expr>          -- 繰り返し式で使用する変数とその初期値を指定
| <expr-with-i-and-acc>               -- 繰り返し適用する式を指定


 反復操作の対象iで指定されたコレクション型のすべての要素について、繰り返し式を適用してacc の値を更新する。

[例]注文商品の価格総額
context Order inv:
sum = ordered_articles->iterate(a: Article; result: Real =0 | result+a.price)

[説明]
注文に関連してロールordered_articlesで指定される商品Article のコレクションのすべての要素a について、価格price の総和がOrderの属性sumの値である。

推論ルール

 推論ルールを記述する場合、if式やimplies式を用いることができる。図2に示したクラス図に基づいて具体例を説明しよう。図2では、顧客クラスCustomerにはhighRisk操作がある。またポートフォリオクラスPortfolioには証拠金操作margin、借入額操作loanValue がある。ポートフォリオには持ち株クラスHoldingが関連している。持ち株クラスには有価証券クラスSecurityが関連している。持ち株クラスの属性に個数numberがある。Securityには価格属性priceがある。Securityのサブクラスとして、ボンドBondとストックStockがある。

図2 計算ルールの例題
図2 計算ルールの例題

[例]リスク評価操作の判断ルール
context Customer::highRisk ():Boolean
post: result =
       if (possession.margin ()> 0 .90* possession.loanValue ())
             then true
             else false
       endif

[説明]
Customer の操作highRisk の結果をCustomer とロールpossession で関連するポートフォリオの証拠金操作margin と借入額操作loanValue に関する次の比較条件で定義している。

 possession.margin()> 0 .90 *possession.loanValue()のときtrueであり、そうでないときfalseである。

[例]売り注文の条件
context Order inv:
type = #sell
implies (portfolio.holdings->exists(Security.name = self.security.name)

[説明]
注文Order の属性type の値が売り(sell)であるとき、関連するポートフォリオの中に注文の有価証券(security)と同じ名前の有価証券がなくてはならない。

計算ルール

 [例]Portfolio の借入額の計算式
Context portfolio::loanValue ()
:Real
post: result =
      holdings->select (
            security.oclType = Bond orsecurity.oclType = Stock )
      ->iterate (h: Holding; value :Real =0 ;
             if (h.security.oclType=Bond )
                  then value + (number *security->price * 0 .90 )
                  else value + (number *security->price * 0 .60 )
             endif
      )

[説明]
select演算子で有価証券Securityの種別がBondまたはStockであるものを選択する。この結果としての有価証券のコレクションに対して借入額をBondとStockに応じてそれぞれ90%と60%で計算して、借入額の総額をイテレータで求めている。

OCLによる事務計算ルールの留意点

 事務処理では、表計算が必要になることが多い。たとえば、業務別に毎日の売上があり、ある月の売上全体を集計する場合などは、2次元の売上表で管理すると思われる。OCLのデータ構造には、2次元配列がないので、どうすればいいだろうか?

 まず、売上Sales クラスと商品Product クラスを定義し、売上クラスの属性として集計用にsum を定義する。商品クラスの属性として、価格priceだけでなく業務種別departmentと売上月monthを持たせ、売上クラスと商品クラスに関連saled_productを定義する。そこで問題なのは、売上集計操作に対する操作制約をOCLでどう記述するかということである。配列構造だと、たとえば、列を業務種別とし、行を日付に対応させることで、行や列ごとに集計するだろう。

OCL では売上クラスの属性としてsumを用意して、sumの値に関する属性制約をiterate演算子を用いて、次のように指定する。

context Sales inv:
sum = saled_products->iterate (a:Product ; result: Real =0 |
  if (a.months = m )and (a.department = d )then result + a.price endif
  )

 ここでは指定された月に属する日付と業務種別を持つ売上オブジェクトの売上属性値だけを加算するように記述している。

 ここで注意する点は、売上集計表の場合は、月別売上や業務別売上を集計した値の合計欄が同じ表の中に出てくるけれども、売上クラスSalesの場合は、月別や業務別の集計結果は、製品の毎日の売上であるProductの属性とは異なるクラスの属性として扱っていることである。OCLでは、コレクション型やiterate演算しかないので、多次元の配列が扱えないように思われるかもしれないが、このようにすれば、従来の配列構造の操作に対する制約ルールもOCLで記述することができる。

 配列構造の場合は、行や列の番号によって通常の売上欄か、集計欄かを区別するが、オブジェクト指向では、クラス属性や関連する別クラスを用意することで、同じ表の中にあるけれども、性格の異なる情報を扱う必要がある。見方を変えれば、意味的に異なるものを、単に表示上同じ形式であるからといって、同一の情報構造の中に埋め込む必要はないはずなので、オブジェクト指向の方が論理的であるともいえるだろう。逆に、従来手法による問題の解き方としての配列構造に着目してオブジェクト指向で表現しようとすると、かえって複雑な計算を持ち込んでしまうことになるかもしれないので、注意する必要がある。

 また、制約を定義する場合、OCLではクラスの属性の値に関する制約式を記述する方法と、属性値を計算するための操作に関する事前条件と事後条件で制約式を記述する方法がある。たとえば、総和を記録するためのsum属性とその値を計算するための操作totalPriceがあるとすると、両者の制約式が等価であることを検証する必要がある。

 OCLで記述されたビジネスルールを検証するために、ドレスデン大学ではOCL コンパイラやOCLツールキットを開発している[3]。このようなOCL開発環境では、ビジネスルールの検証を自動化したり、クラス操作に対する宣言的な仕様を実行できる。したがって、クラス図に対してOCLで制約条件が十分に定義できる場合、クラス属性や操作の意味が一意に定まるので、UMLで記述されたビジネスモデルをOCLにより直接実行できるようになる。

まとめ

 本稿ではUMLを用いたビジネスモデリング手法として代表的なエリクソンとペンカー法のビジネスルールの記述で必要になるOCLについて紹介した。

 エリクソンとペンカー法では、ビジネスルールを導出ルール、制約ルール、存在ルールの3種類に分類している。UML 図式ではクラス間の関係や多重度ならびにクラス操作などを用いて制約ルールや存在ルールを記述できる。しかし、計算ルールと推論ルールからなる導出ルールをUML 図式では記述できない。OCLでは導出ルール、操作制約、ガード式など、主に属性や操作の意味を宣言的な関数型言語の構文を用いることにより形式的に定義することができる。これをまとめて表4に示す。

表4 ビジネスルールの記述
表4 ビジネスルールの記述

 OCLを用いて実際にビジネスルールを記述していく上では、関数型の構文や、コレクション型とイテレータの考え方に習熟することが重要になると思われる。業務アプリケーションでは関係データベースが典型的な例であるけれども、多次元の配列構造が良く出てくるので、UMLでそれらをどのように表現しておけば、OCLによる属性や操作の意味を定義しやすいかということを明確にしておく必要があるだろう。

 次回は引き続き、エリクソンとペンカーによるビジネスパターンの分類と代表的なビジネスパターンを紹介する。

参考文献
[1] エリクソン、ペンカー著、鞍田、本位田監訳、UML によるビジネスモデリング、ソフトバンクパブリッシング、2002 .
[2] Object Constraint Language Specification V 1.1, http://www.omg.org/docs/ad/97-08-08.pdf
[3] Dresden OCL Toolkit, http://dresden-ocl.sourceforge.net/

●執筆者にメールを出す
yamamotosui@nttdata.co.jp
連載第16回へ >>
・連載第1回

・連載第2回

・連載第3回

・連載第4回

・連載第5回

・連載第6回

・連載第7回

・連載第8回

・連載第9回

・連載第10回

・連載第11回

・連載第12回

・連載第13回

・連載第14回

・連載第15回

・連載第16回

・連載第17回