This is version . It is not the current version, and thus it cannot be edited.
[Back to current version]   [Restore this version]

関数を作ってはいけない#

関数というのは、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メソッド禁止」の中で詳細に説明しているので参照して下さい。

例外的に関数を許す場合#

次の二つのケースでは例外的に関数を許します。
  1. メイン関数
  2. 別メモリ空間で稼働するシステムとの間でオブジェクトが持つデータを受け渡す場合
例外的にとは書きましたが、これらの事例は多く存在します。

メイン関数#

OSがアプリケーションを起動する場合、メイン関数が起点となります。OSは、起動パラメータをデータとしてメイン関数に渡します。この起動パラメータ(データ)とメイン関数(処理)は分離されざるを得ません。そのため例外となります。
メイン関数の中には必要最低限の処理のみを記述するようにし、業務用クラスのインスタンスに処理を早く委譲すべきです。

別メモリ空間で稼働するシステムとの間でオブジェクトが持つデータを受け渡す場合#

二つ目の例外がこれですが、抽象的な言葉過ぎて解りにくいと思うので具体例を使って説明します。
上記のように「受注伝票」と「発注伝票」の2つのクラスがあるとします。それぞれのクラスにはメソッドがあります(実際の開発では上記以外のメソッドも必要になります)。
この時、実装すべきクラスは次のように2つです。
ところが良く考えると、
  • クラスの属性をRDBに書き込む
  • クラスの属性をネットワークに出力する
  • クラスの属性をテキストファイルに書き込む
というメソッドは他のクラスにも共通して必要です。システム規模によりますが、最低でも数十、大きい場合は数百のクラスに実装する必要が出てきます。
この3つのメソッドに共通しているのは、データを書き込む先が全て、これらのオブジェクトが稼働しているのとは別のメモリ空間で動いているシステムです。別のメディアと言ってもいいでしょう。
メディアメモリ空間
RDBRDBMS
ネットワークOS
テキストファイルOS

このように、別のメモリ空間で稼働しているシステムに対してはオブジェクトの状態で渡すことが出来ません。そのため一旦データ(属性)のみの状態にして相手側のシステムに渡す必要が出てきます。次の図のように、稼働中のアプリケーションと、外部のメモリ空間で動いているシステムとの間でデータのみを投げ合う形です。
この時、
  • オブジェクトが持っているデータ構造
  • 相手側のシステムとの間で受け渡す処理
の2つが分離されます。この「相手側のシステムとの間で受け渡す処理」を関数(関数)として実装する必要が出てきます。相手に渡せるのはデータのみであるため、そのデータをオブジェクトから受け取って渡す処理が別に必要となるからです。
これをクラス図に描くと次のようになります。RDB関数 が、受注伝票や発注伝票の属性値のみ(データ)をRDBMSとの間で受け渡しします。アプリケーションが稼働しているメモリ空間の外にRDBMSはあります。
※RDB関数は複数のクラスから出来ているため正確にはクラス群です。
Javaの場合、RDB関数の最下層には「JDBCドライバ」(Java Database Connectivity Driver)が配置されます。O/Rマッパ(Object Relation Mapper)がその上位に配置されることが多いのですが、受注伝票や発注伝票などの業務データと切り離されたこれらはすべて関数となります。
しかし、受注伝票や発注伝票の中に「RDBに書き込む」「RDBから読み込む」ようなメソッドを持ち、その中からRDB関数(群)を利用することによって、受注伝票や発注伝票を扱うアプリケーション側からはデータ構造と処理が一体化されている正しいクラスとして扱えます。次のような形です。

RDB関数としてO/Rマッパをもし利用すると次のようになります。

「層」が増えるほど関数が必要となる#

前項で説明したように、外部のメモリ空間で稼働しているシステムとのやりとりが発生する境界ではデータ構造を持たない関数(関数)が必要になります。これは言い方を換えると、「RDB層」「アプリケーション層」などのような層(Layer)があればその境界線で関数が必要になるということです。次の図を見て下さい。
上の図は、一般的な3層構造アプリケーションの動きのうち、画面の情報をRDBに格納するまでの流れを表しています。層と層の間はオブジェクトをそのまま渡せないため、電文という形でデータを渡します。電文を受け取るプロセスは各層での処理の起点の役割、つまりメイン関数と同等の機能を負います。そのため関数にならざるを得ないのです。例外の1番目で書いたのとほとんど同じ理由です。
例えばアプリケーション層の起点ではHTTP電文を受け取ってオブジェクトへと変換します。この動きをメイン関数と比較すれば、起動パラメータがHTTP電文と替わるだけであることが解ります。
結果として、上記のような層分けを出来るだけ行わない方が本来のオブジェクト指向に近づけます。「データと処理の分離」の発生頻度が減るからです。

添付ファイルの追加

ログイン済のユーザのみが添付ファイルをアップロード出来ます。

添付ファイル一覧

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 10-7-2011 02:16 by ytp.