A Day In The Life

とあるプログラマの備忘録

ビジネス・ルールに合わせた独自データ型を活用する

オブジェクト指向技術を活用していくための考察です。

第1回目はデータ型についてお話です。



開発中に

「顧客マスタの名称が30桁から50桁に変更になりました。」

とか

「金額項目に小数が必要になりました。」

なんて仕様変更で大混乱になったことありませんか?



桁数が変わるぐらいなんてことはないと思って調べてみたら、修正箇所が広範囲でテストに何日もかかってしまった・・・よくある話です。



これはプログラムがデータの物理的な構造に密接に結びついているからだと思われます。

物理的な構造と密接に結びついたプログラムが社会的問題にまで発展した有名な例の1つが「2000年問題」です。

西暦を2桁で扱うという物理的な構造に密接に結びついたためにこの問題が起こったといえます。



上記のことからデータの物理的な構造そのものにプログラムを結び付けてはならないという教訓が得られると思います。



物理的な構造と結びついたプログラムとはどのようなコードなのでしょうか実際の例をあげてみましょう(サンプルコードはJavaに似た擬似言語です)。

class OrderDetail {
 //単価
 int unitPrice;
 //数量
 int qty;
}

この場合、unitPriceに小数が必要になったらunitPriceの型を変更する必要がありますね。

unitPriceがひとつのクラスだけで使われているだけなら問題ないですが他のクラスで使われていればそこにも変更が発生してしまいます。



また以下のような金額計算プログラムがあるとして

int subTotal = unitPrice * qty;

これを間違って

int subTotal = unitPrice + qty;

としてもエラーになりませんしunitPrice = qtyなんて無茶な代入もできてしまいます。

これは上記の金額計算プログラムが整数×整数=整数ということしか表していないからです。



つまり、整数型(int)や文字列型などプログラム言語がはじめから用意している型(基本データ型)はコンピュータが変数をどのように扱うかという実装上の問題を取り扱っているだけであって、実際のビジネスルールは何も表されてないのです。

したがって、このような基本データ型だけを使ったプログラムでは保守性の高いプログラムはつくれません。



ではどうすれば保守性の高いプログラムになるのでしょうか?

それには基本データ型を使わずに独自で新しい型(クラス)を作ってやればいいのです。

先ほどのOrderDetailクラスの場合、単価を表すUnitPrice型、数量を表すQuantity型を新たに作成して以下のように修正します。

class OrderDetail {
 UnitPrice unitPrice;
 Quantity qty;
}

こうすることでunitPriceに小数が必要になっても他のクラスに影響を与えずにUnitPrice型だけを修正することで対応できますね。



また、int型には妥当性検証を埋め込めないのに対し、UnitPrice型には妥当性検証や型に対する操作も埋め込むことができます。

例えばUnitPriceクラスの場合、以下のような操作が考えられます。

class UnitPrice {
 ・・・
 //数量と掛算するメソッド
 Subtotal multiply(Quantity qty);
 //文字列変換
 String toString();
 ・・・
}

multiplyメソッドに注目してください。

単価×数量=金額

というビジネスルールを定義していると同時に単価は数量以外で掛け算できないという制限もかかってますね。

UnitPrice型やQuantity型を使ったことにより、その型がどのように表されるか(数字なのか文字なのか)ではなく、その型で何ができるか(金額を管理する、数量を管理する)に焦点をあわせる事ができ、より明確にフィールドの意味が分かるようになります。



この独自に作成したUnitPriceやQuantityのようなデータ型をユーザー定義型と呼ぶことにします。

ユーザー定義型

ビジネス・ルールに合わせて独自に作成したデータ型(クラス)のこと。
それではもう1つ例をあげてみましょう。

ユーザー定義型を使わないクラスCustomerとUserがあります。

class Customer {
 int id;
 String name;
 String address;
 String city;
 String state;
 String zipcode;
 String phone;
}

class User {
 String userId;
 String password;
 String firstName;
 String familyName;
 String address;
 String city;
 String state;
 String zipcode;
 String phone;
}

2つのクラスに共通するaddress,city,state,zipcodeは住所に関するフィールドです。

もしシステム上でマンション・ビル名を別フィールドで管理したいとなった場合、2つのクラスに修正が必要になります。

これでは拡張性が悪くよい設計とはいえません。

ではこの2つのクラスにユーザー定義型Addressを追加してみます。

class Customer {
 int id;
 String name;
 Address address;
 String phone;
}

class User {
 String userId;
 String password;
 String firstName;
 String familyName;
 Address address;
 String phone;
}

class Address {
 //郵便番号
 Zipcode zipcode;
 //都道府県
 State state;
 //市区町村
 String city;
 //住所詳細
 String detail;
}

Addressクラスを追加することでマンション・ビル名を新たに管理する必要が発生しても修正箇所が1箇所ですみます。(ちなみにAddressクラスのStateとZipcodeもユーザー定義型です。)

では最後にユーザー定義型を使うメリットを整理してみます。

  • 物理的な構造とプログラムを分離することができ保守性・拡張性の高いプログラムを作ることができる。
  • ユーザー定義型を使うことにより、ビジネスルールが明確になる。
  • 型自体に妥当性検証を埋め込むことができるので、誤った比較や演算を未然に防ぐことができる。
中でも「物理的な構造とプログラムを分離する」という考え方は特に重要です。



以上のようにメリットの多いユーザー定義型ですが使いすぎるとO/Rマッピングが複雑になるというデメリットもあるので、複雑さとのトレードオフを考慮して使ってみてはいかがでしょうか。



※この記事と同じ内容をhttp://mamezou.net/modules/xfsection/article.php?articleid=95にアップしました。