関数を作ってはいけない#
関数というのは、Cなどの手続き型言語で言う関数のことです。関数を正確に表現すると、「クラス変数にもインスタンス変数にも一切アクセスしないメソッド」です。次の例が関数です。
/* 姓と名を基に氏名を返す */ public String getFUllName(String surName, String givenName) { return surName + " " + givenName; } /* 消費税額を返す */ public BigDecimal getSalesTaxf(BigDecimal amount) { BigDecimal tax = new BigDecimal(5); return tax.multiply(amount).divide(100, RoundingMode.HALF_UP); }
クラス変数にもインスタンス変数にも一切アクセスしないということは、そのオブジェクトが持つデータ構造に依存しないことになります。
「クラスとはデータ構造」という原則に、これは明らかに反します。関数を作ってしまうとデータ構造と処理が分離されてしまい、保守性が下がってしまいます。
ユーティリティクラスを作ってはいけない#
関数を作ってはいけないのだから、ユーティリティクラスと世間で言われるクラスも当然作ってはいけません。ユーティリティクラスというのは関数の単なる集まりであり、データ構造を持たないクラスだからです。ユーティリティクラスと関数の弊害については「privateメソッド禁止」の中で詳細に説明しているので参照して下さい。
例外的に関数を許す場合#
次の二つのケースでは例外的に関数を許します。- メイン関数
- 別メモリ空間で稼働するシステムとの間でオブジェクトが持つデータを受け渡す場合
メイン関数#
OSがアプリケーションを起動する場合、メイン関数が起点となります。OSは、起動パラメータをデータとしてメイン関数に渡します。この起動パラメータ(データ)とメイン関数(処理)は分離されざるを得ません。そのため例外となります。メイン関数の中には必要最低限の処理のみを記述するようにし、業務用クラスのインスタンスに処理を早く委譲すべきです。
別メモリ空間で稼働するシステムとの間でオブジェクトが持つデータを受け渡す場合#
二つ目の例外がこれですが、抽象的な言葉過ぎて解りにくいと思うので具体例を使って説明します。![]() |
この時、実装すべきクラスは次のように2つです。
![]() |
- クラスの属性をRDBに書き込む
- クラスの属性をネットワークに出力する
- クラスの属性をテキストファイルに書き込む
![]() |
メディア | メモリ空間 |
---|---|
RDB | RDBMS |
ネットワーク | OS |
テキストファイル | OS |
このように、別のメモリ空間で稼働しているシステムに対してはオブジェクトの状態で渡すことが出来ません。そのため一旦データ(属性)のみの状態にして相手側のシステムに渡す必要が出てきます。次の図のように、稼働中のアプリケーションと、外部のメモリ空間で動いているシステムとの間でデータのみを投げ合う形です。
![]() |
- オブジェクトが持っているデータ構造
- 相手側のシステムとの間で受け渡す処理
これをクラス図に描くと次のようになります。RDBユーティリティクラスが、受注伝票や発注伝票の属性値のみ(データ)をRDBMSとの間で受け渡しします。アプリケーションが稼働しているメモリ空間の外にRDBMSはあります。
![]() |
![]() |
O/Rマッパ(Object Relation Mapper)がJDBCドライバの上位に配置されることが実際の開発では多いのですが、そのO/RマッパがRDBストレージの代理として表現されることによって、オブジェクト指向により近い実装になります。O/RマッパがJDBCドライバを隠蔽することにより、受注伝票や発注伝票などからは、RDBオブジェクトに処理を委譲している形を取ることが出来ます。
![]() |
「層」が増えるほど関数が必要となる#
前項で説明したように、外部のメモリ空間で稼働しているシステムとのやりとりが発生する境界ではデータ構造を持たない関数が必要になります。これは言い方を換えると、「RDB層」「アプリケーション層」などのような層(Layer)があればその境界線で関数が必要になるということです。次の図を見て下さい。![]() |
例えばアプリケーション層の起点ではHTTP電文を受け取ってオブジェクトへと変換します。この動きをメイン関数と比較すれば、起動パラメータがHTTP電文と替わるだけであることが解ります。Javaサーブレットを使ったWebアプリケーションの場合、サーブレットそのものがこの関数になっています。 次の例はjavax.servlet.http.HttpServletのdoPostメソッドですが、サーブレットアプリケーションにおいてリクエストを電文として受け取る処理の起点になります。レスポンスを戻り値としはいませんが、引数として受け取ったHttpServletResponseオブジェクトに対して返りの電文(データ)を出力する形の関数を実装することになります。
protected void doPost(HttpServletRequest req, HttpServletResponse resp) { }
結果として、上記のような層分けを出来るだけ行わない方が本来のオブジェクト指向に近づけます。「データと処理の分離」の発生頻度が減るからです。
コラム#
このページを読んで、「ユーティリティクラス無しで実際の開発が出来るわけがない」と感じた人は多いでしょう。裏返すとそれは、オブジェクト指向的でない実装が現場でいかに蔓延しているかを表してます。ユーティリティクラス無しで実装するためには以下の二種類のクラスを見逃さずに設計することが必須です。
小物クラスは、システムをまたがった再利用を助長します。一度作れば色々な業務で利用でき、保守性を向上させます。
リンゴ一山クラスは、同じ処理があちこちで実装される危険性を排除します。つい最近(2011年7月)ですが、「Grailsの開発においてリンゴ一山クラスを見逃していた結果、仕様変更の反映が数十ヶ所に及んでしまい工数をロスしました。」と、うちの若手が後悔していました。
これらのクラスをしっかりと設計すれば、「関数とユーティリティクラスっていらないんだ」と気付くことでしょう。
添付ファイルの追加
ログイン済のユーザのみが添付ファイルをアップロード出来ます。
添付ファイル一覧
Kind | Attachment Name | Size | Version | Date Modified | Author | Change note |
---|---|---|---|---|---|---|
png |
utility_class1.png | 5.3 kB | 2 | 01-7-2011 01:01 | ytp | |
png |
utility_class2.png | 6.4 kB | 2 | 01-7-2011 01:02 | ytp | |
png |
utility_class3.png | 8.3 kB | 2 | 01-7-2011 01:02 | ytp | |
png |
utility_class4.png | 4.3 kB | 1 | 02-7-2011 01:24 | ytp | |
png |
utility_class5.png | 4.5 kB | 2 | 02-7-2011 02:07 | ytp | |
png |
utility_class6.png | 30.7 kB | 1 | 05-7-2011 02:05 | ytp | |
png |
utility_class7.png | 9.6 kB | 5 | 31-12-2011 20:50 | ytp | |
png |
utility_class8.png | 12.3 kB | 5 | 31-12-2011 20:50 | ytp | |
png |
utility_class9.png | 7.4 kB | 1 | 22-7-2011 03:29 | ytp |
«
This particular version was published on 11-7-2011 01:54 by ytp.