2016-05-17 に加筆しました。
関数を作ってはいけない#
関数というのは、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メソッド禁止」の中で詳細に説明しているので参照して下さい。
例外的に関数を許す場合#
次の3つのケースでは例外的に関数を許します。- メイン関数
- 異なるクラスを同一の処理で扱いたい場合
- 別メモリ空間で稼働するシステムのオブジェクトが持つデータを受け取る場合
メイン関数#
OSがアプリケーションを起動する場合、メイン関数が起点となります。OSは、起動パラメータをデータとしてメイン関数に渡します。この時、- 起動パラメータ(データ)
- メイン関数(処理)
メイン関数の中には必要最低限の処理のみを記述するようにし、
- 業務用クラスのオブジェクトに処理を早く委譲すべき
異なるクラスを同一の処理で扱いたい場合#
二つ目の例外がこれですが、抽象的な言葉過ぎて解りにくいと思うので具体例を使って説明します。1.何かのメディア(媒体)に読み書きしたい場合#
![]() |
この時、実装すべきクラスは次のように2つです。
![]() |
- クラスの属性をRDBに書き込む
- クラスの属性をネットワークに出力する
- クラスの属性をテキストファイルに書き込む
![]() |
メディア | メモリ空間 |
---|---|
RDB | RDBMS |
ネットワーク | OS |
テキストファイル | OS |
このように、別のメモリ空間で稼働しているシステムに対してはオブジェクトの状態で渡すことが出来ません。そのため一旦データ(属性)のみの状態にして相手側のシステムに渡す必要が出てきます。次の図のように、稼働中のアプリケーションと、外部のメモリ空間で動いているシステムとの間でデータのみを投げ合う形です。
![]() |
- オブジェクトが持っているデータ構造
- 相手側のシステムとの間で受け渡す処理
- 相手側のシステムとの間で受け渡す処理を関数として実装する
- そのデータをやりとりする処理を独立させた方が効率が良い場合が多い
これをクラス図に描くと次のようになります。RDBユーティリティクラスが、受注伝票や発注伝票の属性値のみ(データ)をRDBMSとの間で受け渡しします。アプリケーションが稼働しているメモリ空間の外にRDBMSはあります。
![]() |
![]() |
O/Rマッパ(Object Relation Mapper)がJDBCドライバの上位に配置されることが実際の開発では多いのですが、そのO/RマッパがRDBストレージの代理として表現されることによって、オブジェクト指向により近い実装になります。O/RマッパがJDBCドライバを隠蔽することにより、受注伝票や発注伝票などからはRDBMSオブジェクトに処理を委譲している形を取ることが出来ます。
![]() |
2.異なるクラスをComparatorによって比較したい場合#
JavaのAPIで提供されているjava.util.Comparatorインタフェースのcompare()メソッドは関数です。public int compare(T o1, T o2)
- Comparableインタフェースを実装していないオブジェクトを比較したい
- 比較したいオブジェクトが異なるクラスである

この項で説明した関数は、オブジェクト指向の前身である「構造化言語」の特徴を引き継いだものです。このページのタイトルは「関数禁止」となっていますが、それは安易に関数を量産してしまうことを戒めているのであって、上記のように理解した上で使うことは問題ありません。
別メモリ空間で稼働するシステムのオブジェクトが持つデータを受け取る場合#
前項で説明したように、外部のメモリ空間で稼働しているシステムとのやりとりが発生する境界では、オブジェクトではなくデータのみをやりとりする必要があります。これは言い方を換えると、- RDB層
- アプリケーション層
- 層(Layer)があればその境界線で関数が必要になる
![]() |
- 電文という形でデータ構造を渡す
- 各層での処理の起点の役割、つまりメイン関数と同等の機能
例えばアプリケーション層の起点ではHTTP電文を受け取ってオブジェクトへと変換します。この動きをメイン関数と比較すれば、起動パラメータがHTTP電文と替わるだけであることが解ります。Javaサーブレットを使ったWebアプリケーションの場合、サーブレットそのものがこの関数になっています。 次の例はjavax.servlet.http.HttpServletのdoPostメソッドですが、サーブレットアプリケーションにおいてリクエストを電文として受け取る処理の起点になります。レスポンスを戻り値としてはいませんが、引数として受け取ったHttpServletResponseオブジェクトに対して返りの電文(データ)を出力する形の関数を実装することになります。
protected void doPost(HttpServletRequest req, HttpServletResponse resp) { }
結果として、
- 層分けを出来るだけ行わない方が本来のオブジェクト指向に近づける
まとめ#
- クラス変数にもインスタンス変数にもアクセスしない処理(つまり関数)を作ってはいけない
- ただし以下は例外
- メイン関数
- 異なる複数のクラスを同列視したい場合
- 「層」の間
コラム#
このページを読んで、「ユーティリティクラス無しで実際の開発が出来るわけがない」と感じた人は多いでしょう。裏返すとそれは、オブジェクト指向的でない実装が現場でいかに蔓延しているかを表していることになります。ユーティリティクラス無しで実装するためには以下の二種類のクラスを見逃さずに設計することが必須です。
小粒クラスは、システムをまたがった再利用を促進します。一度作れば色々な業務で利用でき、保守性を向上させます。
リンゴ一山クラスは、同じ処理があちこちで実装される危険性を排除します。
これらのクラスをしっかりと設計すれば、「関数とユーティリティクラスっていらないんだ」と気付くことでしょう。
添付ファイルの追加
ログイン済のユーザのみが添付ファイルをアップロード出来ます。
添付ファイル一覧
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 page (revision-42) was last changed on 14-8-2016 22:57 by ytp