技術レポート tag:www.arksystems.co.jp,2010-11-22:/closeupit//5 2016-08-02T02:43:05Z 顧客・技術動向から製品比較・構築・導入事例まで! IT基盤構築・運用のプロによる技術レポート 実際にシステムを構築・運用している当社技術者が各テーマを担当。IT関連の多種多様な情報が溢れるなか、システム構築や製品選定をする際の判断材料や基準となる情報を整理し、現場担当者ならではの視点でレポートしていきます。 Movable Type Pro 6.3.2 業務シナリオで考える tag:www.arksystems.co.jp,2014:/closeupit//5.810.000810 2014-11-11T16:00:00Z 2016-08-02T02:43:05Z 実際の業務に近い形で考えてみる 次のような業務シナリオがある場合のモデルを... ウェブマスター 実際の業務に近い形で考えてみる 次のような業務シナリオがある場合のモデルを考えてみましょう。 入荷の予定を記入する伝票がある。入荷予定は以下の情報を持つ。 伝票番号 入荷予定日 仕入元会社(取引先) 入荷状態(未入荷/一部入荷/入荷済 のいずれか) 複数の商品の明細(商品名、個数) 商品それぞれが入荷した場合のために次の情報も必要となる。 入荷日 入荷状態(未入荷/入荷済) 上記は、商品ごとに分納が可能である。(商品Aは届いたが、商品Bは未入荷) その上で次の機能が必要となる。 全入荷伝票を検索・一覧表示する画面機能 入荷伝票を登録するための画面機能 ある日に届いた商品の全明細を表示する画面機能 ある日に届いた商品の全明細を表示する帳票機能 上記前提で次のものを記述しなさい。 分析クラス図 設計クラス図 ある日に届いた商品の全明細を表示する帳票機能のシーケンス図 ただし、画面あるいは帳票の機能部分はそれぞれ一つのクラスとして省略設計して構わない。 オブジェクト図 何はともあれ業務分析はオブジェクト図からです。 オブジェクト図を描く時のコツは、複数あり得るオブジェクトは必ず複数描くことです。以下の例で言えば、 入荷伝票 商品 がそれです。  分析クラス図 続いて、クラス名だけのクラス図を描いてみます。クラスにするものの説明を参考にして、 入荷明細クラス を忘れないように定義します。分析をする際に見落としがちな点は次です。 採番する際に伝票番号一覧が必要となる 伝票入荷状態と明細入荷状態は異なるクラスである 入荷明細一覧画面は入荷伝票ではなく入荷明細一覧を扱う 特に3は大切です。入荷明細一覧画面は入荷伝票が何であるか関係なく入荷明細を一覧表示する必要があります。入荷明細一覧クラスが無いと、一覧というデータ構造(同じオブジェクトが複数ある形)に対する処理が複数箇所に分散して実装されてしまい保守性が下がります。 尚、図中に青色を付けてあるのが画面・帳票機能のクラスです。 設計クラス図 続いて属性と処理を記述し、分析クラス図を洗練して設計クラス図を仕上げます。 尚、次のような帳票を出力する前提で処理を定義しました。 下の図が設計クラス図です。分析クラス図を描く段階で一覧系のクラスさえ見逃さなければそれほど難しくないことが判ると思います。 いくつかポイントを挙げます。 入荷伝票クラスと入荷明細一覧クラスの「次の明細を返す」 上記は、一覧系のクラスに必ず必要なメソッドです。Gofのデザインパターンの中の「Iterator(イテレータ)パターン」と同様です。※ 伝票番号一覧クラスの「最近採番値」 上記は、直近の伝票番号を記録するための属性です。 日付クラスの「日付の文字列を返す」 上記は、"2012年03月02日"というような文字列を返すメソッドです。帳票だけではなく、画面やバッチ機能でも利用します。 個数クラスの「単位付き個数を返す」 上記は、"123個"というような文字列を返すメソッドです。帳票だけではなく、画面やバッチ機能でも利用します。 ※Iteratorパターンには他に、    ・次の明細は存在するか  というメソッドが必要ですが、ここでは省略しています。 ある日に届いた商品の全明細を表示する帳票機能のシーケンス図 設計クラス図で定義したメソッドを組み合わせてシーケンス図にします。 ループしながら入荷明細を取得して、それぞれの値を出力する動きになります。 慣れないうちは、シーケンス図を描きながら設計クラス図を考えても構いません。両者を行ったり来たりしながら仕上げていきます。 慣れてくれば、クラス図に定義したメソッドに不足が無いのかをシーケンス図を使って確認するような作業になっていきます。 つまり、シーケンス図はクラス図の作成や説明を補足する意味合いが強いため、全てのシーケンス図を記述する必要は必ずしもありません。 まとめ 業務モデルを作成する際にはまず実例を考える(オブジェクト図) 分析段階から一山クラス(一覧系クラス)を忘れない コラム 実際の業務のモデルを作成する際も、原則を忘れなければ難しくないことを理解してもらえたのではないかと思います。特に、 一山クラス(一覧系クラス) 小粒クラス(日付、個数など) の2つを見逃さなければ、従来から存在する正規化手法の延長線上にあるという考えで概ね間違っていません。 これまで皆さんが携わってきたシステムを題材にして、分析クラス図を描いてみることをお薦めします。 どのメソッドをどのクラスで実装すべきか tag:www.arksystems.co.jp,2014:/closeupit//5.800.000800 2014-09-24T18:00:00Z 2014-09-25T03:30:09Z メソッド実装場所の原則は2つだけ 例えば Aクラス Bクラ... ウェブマスター メソッド実装場所の原則は2つだけ 例えば Aクラス Bクラス という2つのクラスがある場合、 両方の属性を参照しながら処理するメソッドはどちらのクラスに実装すべきなのか? というのがこの章でのテーマです。 メソッドはパラメータ(引数)を持つので、必要なオブジェクトをパラメータで渡すようにすればどのクラスのメソッドとしても実装できてしまいます(AでもBでも実装できる)。 しかしそれでは、 第三者が保守する時に解りづらい 仕様変更に弱い という状態になりがちです。 保守しづらくなる理由は規則性がないからです。そして仕様変更に弱いのは、クラス間でオブジェクトを不規則に渡していると変更の影響が思わぬクラスに出てしまうからです。 原則は2つしかありません。 ヘッダと明細のような上下関係がクラスにある場合、必要な属性の最上位に当たるクラスのメソッドとする クラスの関係が対等または並列である場合、第3のクラスを作ってそのメソッドとする(Mediatorパターンの利用) 順に見ていきましょう。 必要な属性の最上位に当たるクラスのメソッドとする この原則は簡単です。受注伝票で考えてみます。 受注伝票は、商品ごとの受注個数と商品単価を明細として持ちます。この時、 ある受注における受注総額を返すメソッド をどのクラスに実装すべきかを検討してみましょう。分析クラス図は次です。 受注総額を求めるためには、 明細ごとの受注額を計算する(受注個数 × 商品単価) 全明細の受注額を加算する 受注額を返す という流れになります。関係するクラスは、 金額 商品単価 商品単価一覧 受注個数 受注明細 受注明細一覧 受注伝票 と7つありますが、受注総額を返すメソッドはどのクラスで実装するべきでしょうか? 答えは簡単で、 関連するクラスのうち最上位のクラス、つまり受注伝票クラス です。シーケンス図を見て下さい。 ※シーケンス説明の*印は繰り返す処理を意味します。 受注伝票を起点にして処理が始まっているのが解ると思います。 よくある誤りは、受注伝票クラス内にループ処理を実装してしまうことです。シーケンス図にある 受注明細の数だけXXする という部分です。 ループ処理が必要になるのは受注明細一覧が受注明細を複数持っているからですが、その実体が 配列 List Map その他 のいずれなのかというのは隠蔽されているべきです。 その実体を受注伝票にさらしてしまう実装をすると、複数の受注明細を格納するデータ構造が仕様変更によって変わった時、例えばListからMapに変わったような場合に受注伝票も変えなくてはならなくなります。 これを避けるために、そのデータ構造を持っている受注明細一覧の中でループ処理を実装します。つまり、 複数の受注明細というデータ構造の最上位のクラスが受注明細一覧クラス なのでここで実装することになります。 さらに、 受注明細一覧クラスから受け取った金額を受注伝票ラスが受注総額として返す ようにします。 ここで注意すべきなのは、受注伝票クラスの「受注総額を返すメソッド」が必要ないと判断する勘違いです。 受注伝票を扱うアプリケーションに対して受注明細一覧オブジェクトを返せば一見うまくいくように見えますが、受注明細一覧オブジェクトだけではどの受注に対する明細なのかを特定できません。そのため、受注明細一覧に委譲する形で受注伝票が「受注総額を返すメソッド」を持つべきなのです。 結果的に設計クラス図は次のようになります。 第3のクラスを作ってそのメソッドとする(Mediatorパターン) 2つのクラスに上下関係にあたるものがない場合、その2つを仲介する3つ目のクラスを作ります。この原則は、GofのデザインパターンにおけるMediatorパターンに応用されています。 2つのクラスの関係が密接ではない場合に相手のメソッドを直接呼び出す設計をしていると、どちらかのクラスの仕様変更によってもう一方のクラスも修正しなくてはならない事態に陥りがちです。 商品の入荷と在庫を例に考えてみます。必要になるのは、 入荷伝票 在庫商品一覧 の2つです。(下図) 実際の業務では、入荷された商品を倉庫に保存することになります。この時、システム的には入荷伝票を基に在庫数を増やす処理が必要になりますが、この処理(メソッド)をどのクラスで実装するかというのがこの節での命題です。 考えられるのは、 入荷伝票クラス 在庫商品一覧クラス 第3のクラス の3通りです。 入荷伝票に実装する場合は、在庫商品一覧クラスをそのメソッドに引数として渡すことになります。 この時そのメソッド内では、 入荷明細一覧内の入荷明細の数だけ以下を繰り返す 在庫商品インスタンスを生成する 商品、個数、ロケーション、入庫日付を在庫商品に設定する 在庫商品インスタンスを在庫商品一覧インスタンスに追加する というように実装することになります。入荷伝票クラスは在庫商品一覧クラスの仕様を熟知している必要があります。 逆に在庫商品一覧クラスで実装する場合は、入荷伝票クラスをそのメソッドに引数として渡すことになります。 この時そのメソッド内では、 入荷伝票から入荷明細一覧を取り出す 入荷明細の数だけ以下を繰り返す 在庫商品インスタンスを生成する 商品、個数、ロケーション、入庫日付を在庫商品に設定する 在庫商品インスタンスを自身(在庫商品一覧)のインスタンスに追加する というように実装することになります。今度は逆に、在庫商品一覧クラスは入荷伝票クラスの仕様を熟知している必要があります。 入荷の結果である入荷伝票と在庫の結果である在庫商品一覧が密接に結びついても問題ない場合は上記のいずれかでも構わないのですが、相手側クラスの仕様に変更があった場合はその影響を免れません。 こういう場合は、 第3のクラス を設計しておく方が、片方のクラスの仕様変更を相手クラスに影響させずに済みます。Gofのデザインパターンにおける Mediator(仲介者)パターン はこの原則を応用したものです。 この時の処理シーケンスは次のようになります。 ※シーケンス説明の*印は繰り返す処理を意味します。 入荷伝票と在庫商品一覧の仲介者として入荷在庫クラスが働いていることが解ります。この手法を理解する上での注意点は、入荷伝票と在庫商品一覧の両方のデータを持つクラスとして入荷在庫クラスを設計することです。 単なる仲介者として設計してしまうと「仲介する」処理だけを実装することになります。そうではなく、 荷伝票と在庫商品一覧の両方のデータをひとかたまりとしたクラスである と強く意識して設計して下さい。 この節の冒頭にも書きましたが、入荷伝票と在庫商品一覧をあえて強く結びつけたい場合はいずれかのメソッドとして設計して下さい。 仲介者を通じて疎結合にするのか 直接呼び出す密結合にするのか いずれの方法を採るのかは設計者の意図によって変わってきます。しかし迷った場合は第2の原則に従いましょう。 まとめ どのメソッドをどのクラスで実装するのかの原則は2つ。 ヘッダと明細のような上下関係がクラスにある場合、必要な属性の最上位に当たるクラスのメソッドとする クラスの関係が対等または並列である場合、第3のクラスを作ってそのメソッドとする(Mediatorパターンの利用) コラム メソッド実装の原則が2つしかないというのはにわかには信じがたいかも知れません。複雑な関連を持つクラス図を考えるともっとたくさんありそうだと感じるのも無理はありません。 しかし複雑な関連も紐解いていけば、最終的には2つのクラスの関係に分解できます。その2つのクラスにまたがる処理を考える場合、その2つのクラスが 上下関係がある 並列関係になっている の2パターンに分類できるのは理解できることでしょう。 このように、一見複雑なものもその原則は単純であることが多いのです。複雑に絡み合った結果であるパターンの奥には単純な原則が隠れています。それを見抜く力を付けることが一番大切だと私は感じています。 Privateメソッド禁止 tag:www.arksystems.co.jp,2014:/closeupit//5.799.000799 2014-09-24T17:00:00Z 2014-10-31T02:23:47Z privateメソッドを作りたくなった時は存在するべきクラスを見逃している ... ウェブマスター privateメソッドを作りたくなった時は存在するべきクラスを見逃している このページの見出しを見て「えっ?!」と思う人は多いでしょう。でも、私がマネージメントする開発では「privateメソッド禁止」は当たり前なんです。 例を使った下の説明で詳しい内容は理解して欲しいのですが、要点だけを言うと、 privateメソッドに渡すパラメータをひとかたまり(データ構造)とするクラスを見逃している ということです。 顧客会社クラスで考えてみる 商取引を扱うシステムで良く出てくる「顧客会社」クラスを使って考えてみます。 担当者姓 担当者名 という属性がこのクラスの中にはあります。そしてこの二つの値を使った 担当者氏名を返すメソッド を作ってみます。姓と名の間にスペースを挟んで返す仕様だとすると、コードは次のようになるでしょう。 public String getTantoushaFullName() {     return this.tantoushaSei + " " + this.tantoushaMei; } 次に、このクラスが仕様変更となり、 代表者姓 代表者名 という属性が追加されたとします。 代表者氏名を返すメソッド もこの時必要になったとすると、以下のようなコードを追加することでしょう。 public String getDaihyoushaFullName() {     return this.daihyoushaSei + " " + this.daihyoushaMei; } そして次に、 いずれの氏名を返す時も、姓と名の間のスペースを2つに替えて欲しい という要望が挙がったとします。よく見てみると上記二つのメソッドは非常に良く似ています。「スペースを挟んで姓と名を連結する」という仕様だからです。こういう時、privateメソッドを作っておくと変更が1ヶ所で済んだはずです。次のようなコードです。 private String getFullName(String sei, String mei) {     return sei + "  " + mei;} public String getTantoushaFullName() {     return getFullName(this.tantoushaSei, this.tantoushaMei);} public String getDaihyoushaFullName() {     return getFullName(this.daihyoushaSei, this.daihyoushaMei); } こうしておけば、間に挟むスペースが3つに替わろうが変更箇所は1ヶ所で済みます。 が、果たして本当にそうでしょうか? 例えば 仕入れ先会社クラス が別にあったとして、その属性にも「担当者姓」「担当者名」があったとしたらどうでしょう? そしてそのクラスにも「担当者氏名を返す」というメソッドが必要で、「姓と名の間にスペースを2つ挟む」という仕様だったら? そのクラスにも同じようなprivateメソッドが必要になってしまい、仕様変更への対応が1ヶ所というわけにいかなくなってしまいます。 データ構造を見逃すな! 上記の例で解るように、 privateメソッドを作りたくなった時はクラスを見逃している のです。 privateメソッドはクラス内の共通関数に当たるのですが、共通関数を作ろうとすれば、その関数に渡すパラメータをそもそもまず共通化する必要があります。 そのパラメータをひとかたまり(データ構造)とするクラスを作るべき なのです。 この場合は、属性として 姓 名 を持つ氏名クラスを作るべきです。そしてそのクラスに委譲するのです。 パラメータを持たないprivateメソッドはもっとだめ 「privateメソッド禁止」の話をする際によくある反論が、「パラメータを持たないprivateメソッドだってあるじゃないか!」というものです。 パラメータの無い、つまり共通関数ですらないprivateメソッドがなぜ必要なのでしょうか? 彼らの答えは「可読性をあげるため長いコードを分割するんだ。」なのですが、これは全くの誤りであると私は考えます。 なぜならコードが長くなるのは、上記のような小物クラスを見逃しまくっていて、結果的に手続き的な記述になっているからだと断言できるからです。クラスという単位で分かれているべきものが一つになってしまっていれば、その中の記述が長くなってしまうのは当然です。 そもそも一連の処理ならばprivateメソッドなどに分割せず、処理の流れの通りに1ヶ所に書いてある方が、第三者が見た時の可読性は高いはずです。下の例を比べてみて下さい。 privateメソッドに分割した例 public String getSomething() {     doSomething1();     doSomething2();     doSomething3(); } public String getAnotherthing() {     他の処理が書いてある; } private void doSomething1() {     何かの処理1-1が書いてある;     何かの処理1-2が書いてある; } private void doSomething2() {     何かの処理2-1が書いてある;     何かの処理2-2が書いてある; } private void doSomething3() {     何かの処理3-1が書いてある;     何かの処理3-2が書いてある; } 一連の処理が1ヶ所に書いてある例 public String getSomething () {     何かの処理1-1が書いてある;      何かの処理1-2が書いてある;     何かの処理2-1が書いてある;      何かの処理2-2が書いてある;      何かの処理3-1が書いてある;     何かの処理3-2が書いてある; } public String getAnotherthing() {     他の処理が書いてある; } 正しいクラスを定義した結果それでもメソッドの記述が長くなる場合、例えば属性が100個ぐらいあって一つの処理が長くなる場合、それはそれで正しいのです。意味も無く分割する必要は無いというのが私の考えです。 オブジェクト指向の原則を考えるとそもそもprivateメソッドは不要 クラスとはデータ構造で説明した次の図をもう一度見て下さい。出典: What Is an Object?(別ウィンドウで開きます)   フィールド(属性)の周りをメソッドが取り巻いている形が見えますが、これは裏返すと、 フィールドを隠蔽するためにメソッドが口を開けている(公開されている) ということです。つまり公開されないメソッドはこの原則に反するのです。 privateによって隠蔽すべきなのはフィールド(属性)であってメソッドではない という原則がこの図からも解ります。 ユーティリティクラスだとどうだ? 「『担当者氏名を返す』メソッドはprivateではなくユーティリティクラスでいいじゃないか?」と思った人もいるでしょう。 次のようなコードです。 public class StringUtility {     public String getFullName(String sei, String mei) {         return sei + "  " + mei;     } } public class KokyakuKaisha {   :   :     public String getTantoushaFullName() {         return StringUtility.getFullName(this.tantoushaSei, this.tantoushaMei);     }     public String getDaihyoushaFullName() {         return StringUtility.getFullName(this.daihyoushaSei, this.daihyoushaMei);     } } 関数とユーティリティクラスは禁止でも書きましたが、このようなユーティリティクラス(インスタンス変数にもクラス変数にもアクセスしないメソッド群)はそもそもオブジェクト指向の原則である データ構造と関数の一体化 を崩してしまいます。その結果どういう事が起きるかを考えてみます。 上記のStringUtility#getFullName()メソッドは単なる関数なので、姓名の連結のみに使われる保証はありません。極端な場合は次のような使われ方をするかもしれません。 public class Jimusho {     private String yuubinBangou = ""; // 郵便番号     private String jyuusho = ""; // 住所      :      :     public String getAtesaki() {         return StringUtility.getFullName(this.yuubinBangou, this.jyuusho);     } } StringUtility#getFullName()メソッドの機能は、「1つめのパラメータと2つめのパラメータを2個のスペースを挟んで連結する」というものです。上記のように、「事務所クラスの宛先を返すメソッド」にこの機能がたまたま使えたとすれば使われてしまう可能性を排除できません。 この時、システムを国際化対応する必要が仮に出てきて、 担当者ミドルネーム 代表者ミドルネーム という属性が追加になったとします。そして、 氏名を返すメソッドは全て『姓 ミドルネーム 名』とする(間のスペースは1つずつ) という要件に替わったとします。 この場合の変更箇所はStringUtility.getFullName()メソッドのみではなく、それを呼び出している全メソッドが対象となります(当然ですが)。その際、「従業員クラスの宛先を返すメソッド」も該当しますが、事務所クラスは「ミドルネーム」を持たないため破綻してしまいます。別の関数を新たに作る必要が出て来るのです。高い保守性とこれでは言えません。 一方で、「氏名クラス」を利用している場合はどうでしょうか。 氏名クラスを利用する場合、そのデータは「姓」と「名」として使うことでしょう。「郵便番号」と「住所」を無理矢理入れようと思えば入れられますが、上記のユーティリティクラスの使われ方ほどの可能性はありません。 なぜならば、ユーティリティクラスの目的は「処理」つまり 1つめのパラメータと2つめのパラメータを2個のスペースを挟んで連結する という機能を提供することですが、氏名クラスの目的は、 氏名を構成する属性(データ構造)を保持すること だからです。誤用の可能性は低いでしょう。 氏名クラスを利用している場合、氏名を返す()メソッドを呼び出している側の修正は必要ありません。このことがシステムの保守性をいかに高くするかを是非理解して下さい。 ひとかたまりとなるデータ構造をクラスに閉じ込め、そのデータに関する処理も併せてその中に閉じ込めてしまうことで修正箇所を最小限にしようというオブジェクト指向の目的がこの例からも解ります。 例外的にprivateメソッドを許す場合 以下の場合のみprivateメソッドが必要になります。 再帰処理を実装する場合 汎用的なデータ構造を持つクラスを継承せずに利用する場合 再帰処理 再帰処理は、メソッドの中からそのメソッド自身を再度呼び出すというアルゴリズムです。 これを利用する際、 外部から呼び出されるメソッド(再帰処理の入り口) 再帰的に呼び出されるメソッド(メソッド内部で何回も呼び出される) という使い分けが必要になる場合があります。この時後者のメソッドをprivateで定義します。 例えば二分探索木を使う場合、走査の起点をルートノードに固定化したいのですが、その処理は外部から呼ばれたpublicなメソッドで行い、再帰的な走査はprivateで行うという使い分けをします。次のようなコードです。 /* 走査 */ public void traverse() {     ascendingTraverse(this.rootNode); } /* 昇順走査 */ private void ascendingTraverse(Node current) {     if (current.getLeftNode() != null) {          // 再帰呼び出し         ascendingTraverse(current.getLeftNode());     }     currentに対する何かの処理;      if (current.getRightNode() != null) {          // 再帰呼び出し          ascendingTraverse(current.getRightNode());     } } 汎用的なデータ構造を持つクラスを継承せずに利用する これは、JavaのArrayListクラスのように汎用的なデータ構造を持つクラスを継承せずに委譲の形で利用する時、その汎用的なクラスに対する特定の扱いを共通化したい場合です。 public class Someclass {     private String name = "XX";     private List<Integer> someList = new ArrayList<Integer>();     :     :     public String firstMethod() {         // リストの初期化         clearList();         何かの処理;         :     }     public String secondMethod() {         // リストの初期化         clearList();         別の何かの処理;         :     }     /** リストの要素をゼロで置き換える **/     private void clearList() {         for (Integer el : someList) {             el = new Integer(0);         }     } この場合のprivateメソッドは、汎用的なデータ構造を持つクラスを継承するならば必要なくなります。上の例ではArrayListクラスです。ですが、汎用的に出来ているクラスの継承においては、そのクラスのデータ構造を熟知していないと思わぬ副作用に出くわすデメリットもあるため、privateメソッド利用とのデメリットをよく検討した上で選ぶようにして下さい。 まとめ privateメソッドを作っていけない ただし以下は例外 再帰処理 汎用的なデータ構造を持つクラスを継承せずに利用する場合 コラム 2000年頃の話ですが、私が初めて仕事でJavaを使ったプロジェクトにて、私は帳票用の自動集計フレームワークを設計・実装していました。 そのプロジェクトの本番稼働後に仕様追加の依頼があってフレームワークのコードを直そうとしたところ、同じ修正を2ヶ所にしなければならない事が判明しました。そのフレームワークの実装に当たっては、「1ヶ所直せば皆直る」を心掛けていたのにもかかわらずです。 私は必死になって理由を考えました。そしてこのページで説明した「privateメソッドが癌だった」ことに気付いたのです。その時は新しいクラスを作ってリファクタリングし、仕様変更に無事対応できました。 オブジェクト指向の出発点は「1ヶ所直せば皆直る」です。難しいことは置いておいても、これを目指して皆さんも設計・実装してみて下さい。そうすれば、オブジェクト指向の核心がきっと見えてきます。 関数とユーティリティクラスは禁止 tag:www.arksystems.co.jp,2014:/closeupit//5.781.000781 2014-09-24T16:00:00Z 2014-09-25T02:31:44Z 関数を作ってはいけない 関数というのは、Cなどの手続き型言語で言う関数のこ... ウェブマスター 関数を作ってはいけない 関数というのは、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は、起動パラメータをデータとしてメイン関数に渡します。この時、 起動パラメータ(データ) メイン関数(処理) の二者は分離されざるを得ません。そのため例外となります。 メイン関数の中には必要最低限の処理のみを記述するようにし、業務用クラスのオブジェクトに処理を早く委譲すべきです。 異なるクラスを同一の処理で扱いたい場合 二つ目の例外がこれですが、抽象的な言葉過ぎて解りにくいと思うので具体例を使って説明します。 上記のように「受注伝票」と「発注伝票」の2つのクラスがあるとします。それぞれのクラスにはメソッドがあります(実際の開発では上記以外のメソッドも必要になります)。 この時、実装すべきクラスは次のように2つです。 ところが良く考えると、 クラスの属性をRDBに書き込む クラスの属性をネットワークに出力する クラスの属性をテキストファイルに書き込む というメソッドは他のクラスにも共通して必要です。システム規模によりますが、最低でも数十、大きい場合は数百のクラスに実装する必要が出てきます。 この3つのメソッドに共通しているのは、データを書き込む先が全て、これらのオブジェクトが稼働しているのとは別のメモリ空間で動いているシステムです。別のメディアと言ってもいいでしょう。 メディア メモリ空間 RDB RDBMS ネットワーク OS テキストファイル OS このように、別のメモリ空間で稼働しているシステムに対してはオブジェクトの状態で渡すことが出来ません。そのため一旦データ(属性)のみの状態にして相手側のシステムに渡す必要が出てきます。次の図のように、稼働中のアプリケーションと、外部のメモリ空間で動いているシステムとの間でデータのみを投げ合う形です。   この時、 オブジェクトが持っているデータ構造 相手側のシステムとの間で受け渡す処理 の2つが分離されます。つまり、 相手側のシステムとの間で受け渡す処理を関数として実装する 必要が出てきます。相手に渡せるのはデータのみであり、 そのデータをやりとりする処理を独立させた方が効率が良い場合が多い からです。 これをクラス図に描くと次のようになります。RDBユーティリティクラスが、受注伝票や発注伝票の属性値のみ(データ)をRDBMSとの間で受け渡しします。アプリケーションが稼働しているメモリ空間の外にRDBMSはあります。 Javaの場合、RDB関数の最下層には「JDBCドライバ」(Java Database Connectivity Driver)が配置されます。 しかし、受注伝票や発注伝票の中に「RDBに書き込む」「RDBから読み込む」ようなメソッドを持ち、その中からRDB関数(群)を利用することによって、受注伝票や発注伝票を扱うプログラムクラス側からはデータ構造と処理が一体化されている正しいクラスとして扱えます。次のような形です。 O/Rマッパ(Object Relation Mapper)がJDBCドライバの上位に配置されることが実際の開発では多いのですが、そのO/RマッパがRDBストレージの代理として表現されることによって、オブジェクト指向により近い実装になります。O/RマッパがJDBCドライバを隠蔽することにより、受注伝票や発注伝票などからはRDBMSオブジェクトに処理を委譲している形を取ることが出来ます。 別の例を見てみます。 JavaのAPIで提供されているjava.util.Comparatorインタフェースのcompare()メソッドが関数となります。 public int compare(T o1, T o2) o1およびo2オブジェクトのクラスそのものがjava.lang.Comparableインタフェースを実装していれば、Comparatorインタフェースによる比較は必要ありません。しかし次のような時があります。 Comparableインタフェースを実装していないオブジェクトを比較したい 比較したいオブジェクトが異なるクラスである 上記のような場合は自作のComparatorを作る必要が出て来ます。次のようなイメージです。 別メモリ空間で稼働するシステムのオブジェクトが持つデータを受け取る場合 前項で説明したように、外部のメモリ空間で稼働しているシステムとのやりとりが発生する境界では、オブジェクトではなくデータのみをやりとりする必要があります。これは言い方を換えると、 RDB層 アプリケーション層 などのような 層(Layer)があればその境界線で関数が必要になる ということです。次の図を見て下さい。 上の図は、一般的な3層構造アプリケーションの動きのうち、画面の情報をRDBに格納するまでの流れを表しています。層と層の間はオブジェクトをそのまま渡せないため、 電文という形でデータ構造を渡す ようになっています。電文を受け取るプロセスは、 各層での処理の起点の役割、つまりメイン関数と同等の機能 を負います。そのため関数にならざるを得ないのです。例外的に関数を許す場合の1番目で書いたのとほとんど同じ理由です。 例えばアプリケーション層の起点ではHTTP電文を受け取ってオブジェクトへと変換します。この動きをメイン関数と比較すれば、起動パラメータがHTTP電文と替わるだけであることが解ります。Javaサーブレットを使ったWebアプリケーションの場合、サーブレットそのものがこの関数になっています。 次の例はjavax.servlet.http.HttpServletのdoPostメソッドですが、サーブレットアプリケーションにおいてリクエストを電文として受け取る処理の起点になります。レスポンスを戻り値としてはいませんが、引数として受け取ったHttpServletResponseオブジェクトに対して返りの電文(データ)を出力する形の関数を実装することになります。 protected void doPost(HttpServletRequest req, HttpServletResponse resp) { } 結果として、 層分けを出来るだけ行わない方が本来のオブジェクト指向に近づける のです。「データと処理の分離」の発生頻度が減るからです。 まとめ クラス変数にもインスタンス変数にもアクセスしない処理(つまり関数)を作ってはいけない ただし以下は例外 メイン関数 異なる複数のクラスを同列視したい場合 「層」の間 コラム このページを読んで、「ユーティリティクラス無しで実際の開発が出来るわけがない」と感じた人は多いでしょう。裏返すとそれは、オブジェクト指向的でない実装が現場でいかに蔓延しているかを表していることになります。 ユーティリティクラス無しで実装するためには以下の二種類のクラスを見逃さずに設計することが必須です。 小粒クラス リンゴ一山(ひとやま)クラス 小粒クラスは、システムをまたがった再利用を促進します。一度作れば色々な業務で利用でき、保守性を向上させます。 リンゴ一山クラスは、同じ処理があちこちで実装される危険性を排除します。 これらのクラスをしっかりと設計すれば、「関数とユーティリティクラスっていらないんだ」と気付くことでしょう。 リンゴ一個とリンゴ一山は異なるクラス tag:www.arksystems.co.jp,2014:/closeupit//5.776.000776 2014-07-30T18:00:00Z 2014-07-31T02:29:08Z 単位が異なれば同じ物でも異なるクラスになる 「一個売りしているリンゴ」をシ... ウェブマスター 単位が異なれば同じ物でも異なるクラスになる 「一個売りしているリンゴ」をシステムで扱う場合、どのような属性が必要になるかを考えてみます。 品種 重さ 売り値 入荷日 などが考えられます。それでは、「一山(ひとやま)売りしているリンゴ」はどうでしょうか? 品種 (同じ山には同じ品種を乗せる前提) 重さ (一山あたりの総重量) 売り値 (一山あたりの総額) 個数 上記を較べてみると、同じリンゴでも違いのある事が解ります。処理を考えてみるとさらに明らかになります。 「一山売りしているリンゴ」では、 一個あたりの売り値(平均の売り値)を返す というメソッドがすぐに思い浮かびます。 これらから解るように、「リンゴ」というクラスが設計段階、特に分析クラス図で出てきても、 それが一個なのか一山なのかを確定させないと正しいクラスとして設計できない のです。裏を返すと、同じリンゴであっても、 それを扱う単位が異なれば異なるクラスになる ことを理解して設計する必要があるということです。 要件の中に出てくる単位を見逃さない 前項で解るように、単位は非常に重要です。ここで言う単位は物理学で言うものではなく、業務を遂行する人たちがあるものをひとかたまりとして扱う大きさのことです。 一伝票 一明細 一会社 一組織 一箱 一取引 一件 一覧 など色々ありますが、人間が何かをひとかたまりとして扱い、そのかたまりの種類が異なる場合には必ず単位が異なります。もちろん、非常に汎用的な「一個」という単位はあちこちに出てきますが、その場合は「何々一個」というように聞き分けて設計していけば問題ありません。 実際のシステムでは一件クラスと一覧クラス このページの見出しにある「リンゴ一個とリンゴ一山」を実際のシステムではどう考えればいいのでしょう? 答えは簡単で、 『一件』と『一覧』は異なるクラスとして設計・実装する です。『一件』がリンゴ一個に相当し、『一覧』がリンゴ一山に相当します。 例えば業務要件を聞いている中で「顧客会社クラス」が出てきた場合は、 「顧客会社一件クラス」 「顧客会社一覧クラス」 の二つの可能性があります。 顧客会社を一件のみ、つまり一会社として扱う時のクラスが「顧客会社一件クラス」です。顧客会社を複数件で、つまり一覧として扱う場合は「顧客会社一覧クラス」を作ります。そして、 一覧クラスは一件クラスを内部で複数持つ ように設計します。ほとんどのシステムにおいてその両方が必要です。 別の例を出しましょう。入荷予定の結果クラスである「入荷伝票」を考えてみます。 入荷業務で良く出てくる、 入荷伝票 入荷明細 は、「一伝票」と「一明細」というように単位が異なるので別クラスとして定義します。伝票は、ある仕入れ先からのある日の入荷物全体を表現しています。伝票ヘッダとも言われます。明細は商品単位の情報を持っており、一伝票が複数の明細を持つ形です。 さらに、 入荷伝票一覧クラス 入荷伝票一件クラス 入荷明細一覧クラス 入荷明細一件クラス というように、それぞれ一覧と一件というように4つに分けて考えます。 これらのクラスの考え方は次の通りです。「一伝票」「一明細」というように異なる単位で呼ばれる物を見つけたら、それらをさらに「一覧」「一件」というふうに分けて考えます。 モノ 一覧 一件 入荷伝票(ヘッダ) ○ ○ 入荷明細 ○ ○ しかし上の図を見て、次のような設計でいいのではないかと気付いた人がいるかもしれません。伝票一件が明細一覧も兼ねる形です。 実は、 入荷伝票クラスは入荷明細に対する一覧クラスを兼ねている のです。また、 伝票に依存しない明細一覧オブジェクトを形成したい 場合に上記ではうまくいきません。例えば、 全ての伝票の中から商品Aだけの明細一覧を表示する というような要件に対応できないのです。入荷明細一覧クラスから参照できる入荷伝票が1つしかないことになり、 入荷明細に商品Aを持つ複数の入荷伝票 という関係を表現できません。 このため、入荷伝票が入荷明細一覧クラスを兼ねる形で設計しておくようにします。 それとは別に、入荷伝票とは独立して入荷明細を一覧化するためのクラスも設計します。次のような形です。 上記図のカージナリティに注意して下さい。 入荷伝票一件クラスと入荷明細一件クラスは「1:1以上」 入荷明細一覧クラスと入荷明細一件クラスは「1:0以上」 となります。入荷明細が1件も存在しない入荷伝票は許されません。一方で、「商品Aを持つ入荷明細」を検索した場合の結果が0件となることはあり得ます。 RDBならば一レコードと一テーブルが別クラス 永続化装置としてRDBを使うことが実際の開発では日常的です。この時には、RDBテーブル一つに対して 一レコードクラス 一テーブルクラス (複数レコードを扱う時のクラス) を必ず対(つい)で設計・実装するようにします。 一レコードクラスは前項で書いた一件クラスと同じ事です。物や結果の一件分がRDBテーブルの一レコードとして保存されます。 一テーブルクラスというのは少し解りにくいのですが、複数のレコードを扱う時に使います。 複数のレコードというのは最大でそのテーブルの全件を持つ ことになり、言葉を換えると 一テーブル分 となります。これも前項で出てきた一覧クラスと同等です。 関係をまとめると、 一件クラス ⊇ 一レコードクラス 一覧クラス ⊇ 一テーブルクラス となります。それぞれ何が違うかというと、一件クラスまたは一覧クラスのうちRDBに永続化する必要のあるものが一レコードまたは一テーブルという別の呼び方をされることがあるというだけです。永続化される必要の無いものは別名では呼ばれません。 その上で、 RDBから一件を取得する処理 RDBから一覧を取得する処理 をそれぞれのクラスの中に実装します。次のような形です。 この際、入荷伝票一件クラスのRDBから一件を取得する処理の中で、 ひも付く複数件の入荷明細を取得する処理 も実装します。 「複数件」が出てきたら一山クラスを作る RDBテーブルに限らず、同じものを複数件扱う要件が出てきたならば「一山クラス(一覧クラス)」を必ず作ります。これをせずに実装者任せにして何でもかんでもList(配列)に入れて実装してしまうと、いざ仕様変更が発生した時にあちこちを修正することになっていまいます。 Javaで実装する場合、ListやMapなどのコレクション(Collection)を一山クラスの内部で使います。 明細の一覧を画面に表示する場合にある規則で並べたい、例えば商品順に並べたいような場合、一覧クラスの中でTreeMapクラスを使います(ただし入荷商品に重複がない前提)。そうすることで、ソート処理を別途用意する必要がなくなります。明細一覧というデータ構造とソート処理を分離することなく実装できるのです。 ソートに限らず、業務的な処理も一覧の中に実装します。例えば、「数量の一番多い明細を返す」という処理です。 まとめ 単位が換われば異なるクラスにする 複数件を扱う場合は一覧クラスを必ず作る コラム 2011年当時、「Grailsの開発においてリンゴ一山クラスを見逃していた結果、仕様変更の反映が数十ヶ所に及んでしまい工数をロスしました。」と、うちの若手が後悔していました。 リンゴ一山クラスを作成せずに何でもかんでもList(配列)に入れて実装してしまうと、似たような処理があちこちに作られる危険が高くなります。データと処理が分離されてしまうからです。 皆さんの開発現場において設計者がリンゴ一山クラスを見逃しているようならば、このページの内容を参考にして指摘してあげて下さい。 小粒クラス tag:www.arksystems.co.jp,2014:/closeupit//5.775.000775 2014-07-30T17:00:00Z 2014-07-31T02:27:10Z 様々なクラスで再利用可能なのが小粒クラス 粒度が非常に小さいクラスのことを... ウェブマスター 様々なクラスで再利用可能なのが小粒クラス 粒度が非常に小さいクラスのことを小粒(こつぶ)クラスと私は呼んでいます。次のようなものです。 数量 金額 日付 氏名 宛先 クレジットカード番号 電話番号 などなど。 これらは、他のクラスの属性として使われることを前提としています。そのため、極めて汎用的に設計しておく必要があります。その代わり、一度作ってしまえばシステムや時代をまたがって再利用することが可能です。 数値系クラス 業務システムで一番使うのが数値系のクラス群です。まずは以下のクラス図を見てください。 たかだか数量や金額にこんなにたくさんのクラスが出てくるのか、と驚く人もいるでしょう。図に出てくるクラスを列記します。 数値 数量 金額 通貨※ 円貨金額 消費税率一覧 消費税率 外貨金額 レート一覧 レート ※通貨は数値系クラスではありません。 順に見ていきましょう。 【数値】 数値系全てのクラスの最上位に位置するクラスです。数値を取るクラスを汎化して抽出されたクラスです。 内部では、各言語が持っている十進数クラスを利用することになります(Javaではjava.math.BigDecimal)。その上で、 業務上必要な演算処理(例えば四則演算) カンマ区切りの文字列を編集して返す などの処理を実装します。こうすることで、文字列編集用の関数を別途用意する必要がなくなり、さらに、画面・帳票・バッチとありとあらゆるプログラムで共通利用することが可能となります。 【数量】 数量クラスは、 数値 単位 の2つの属性から出来ています。例えば、 3個 10kg 43坪 などの情報を持つことになります。 システムの性格にもよりますが、デフォルトの単位を「個」にしておくと便利でしょう。 そして、 単位付きの文字列を返す という処理を持たせます。上記で書いた例のような文字列そのものが返ってくるのでとても便利です。 場合によっては、「単位」もクラスとして定義しておく必要があります。 【金額】 様々な通貨の金額を汎化したクラスです。このクラスは通貨を持ちます。アプリケーションはこのクラスのオブジェクト(インスタンス)を直接扱うのではなく、円貨金額と外貨金額のオブジェクトを使います。 このクラスは、 通貨記号付きカンマで返す というメソッドを持ちます。例えば、$123,456,789という文字列を返します。 上記クラス図は日本の業務システムで利用する前提で記述してあります。FX取引などのサイト開発では外貨→円貨のみの金額変換ではなく全ての通貨の相互変換が必要になるため、円貨と外貨を区別しないモデルが必要なことに注意してください。 【通貨】 通貨は、金額がどこの国(または地域)の通貨であるかを示すために必要です。このクラスはレート一覧を持ち、指定された日付(あるいは年月)のレートを返す処理を持ちます。 通貨は数値系のクラスではありません。 【円貨金額】 日本の通貨である円の金額を持ちます。そして日本特有の処理である「消費税額を返す」処理を持たせます。こうすることで、消費税計算の関数を別途作る必要がなくなります。消費税率を特定するために消費税一覧クラスを持ちます。 消費税額を計算するためには次の判断が必要になります。 内税 外税 税自身 3番目の「税自身」というのは、消費税額自身を持つインスタンスであることを示します。 内/外税区分 という属性でこれらの値を持ちます。 日本の業務を前提にしたシステムの場合、円貨金額の通貨を明示的に保存しておくことはしません。その場合は金額クラスが通貨クラスを持つのではなく、外貨金額が通貨を持つように設計するといいでしょう。 【消費税率一覧】 複数の消費税を持つ「一山クラス」です。消費税は日付によって税率が異なるため複数あり得るのでこのクラスが必要です。 日付を基にして消費税率を判断し、それを返すメソッドを持ちます。 【消費税率】 消費税率の数値を持ちます。2011年時点の消費税率は5%ですが、この時に 5 0.05 のいずれで持つのかは設計者の判断になります。その持ち方に合わせて、円貨金額クラスの消費税額を返すメソッドを実装します。 【外貨金額】 円貨以外の金額を保持するクラスです。保持している金額値を円貨に換算するための 円貨金額に変換する メソッドを持ちます。このメソッドの中では、通貨クラスの レートを返す メソッドを利用してレートを取得し、計算します。レートを決めるためにはその金額が発生した日付(場合によっては年月)が必要になります。 【レート一覧】 レートを複数持つ一山クラスです。レートは日付によって変わるためこの一覧クラスが必要になります。 日付によってレートを特定するための レートを返す メソッドを実装します。 自国通貨金額(日本では円貨金額)のレート値は常に「1」であるため特殊処理が必要になります。通貨クラスが自国通貨フラグを持っているのはこのためです。 【レート】 為替のレート値を保持するクラスです。このクラスは、 レート値 日付 を持ちます。場合によって日付は年月になることもあります。 日付クラス 日付クラスは何かが実行された年月日を持っているクラスです。年月日は、暦としてあらかじめ定義されているある特定の日を表現するので 物クラス として扱います。業務システムにてよく利用される「営業日マスタ」を想像してもらえば解りやすいと思います。 2011年8月17日の売上金額 2011年8月17日の契約件数 を考えた場合、売上金額と契約件数という違いはあるものの、両方とも同じ2011年8月17日という日のオブジェクトを指しています。 日付クラスはとても汎用的に設計する必要があります。例えば、 区切り文字で日付を編集した文字列を返すメソッド を実装するにしても、 スラッシュ(/)で区切るメソッド ハイフン(-)で区切るメソッド などがいくつか必要になります。また、 翌日の日付を返す 翌月の日付を返す 翌年の日付を返す なども持っていると使う側が便利です。 特に会計システムなどでサイト計算が必要な場合は、 xx日後の日付を返す というメソッドも持ちます。 Javaで実装する場合、java.util.Calendarクラスを日付値として内部で利用します。 日本でのみ使われる前提のシステムの場合はロケールを意識しなくて済みますが、グローバルシステムの場合はシステムが稼働しているロケールも意識して日付編集を行う必要が出て来ます。 その他の例 かつて関わった物流プロジェクトにて体積クラスというのを設計しました。 長さ(Length) 幅 (Width) 高さ(Height) という3つの属性を持ち、3者を乗算して 体積 を返すというものです。 複数の部品を梱包した荷姿が不定型で、出荷する荷姿ごとに体積を管理する必要があったためです。 この体積クラスは結果クラスに分類されます。 まとめ 他のクラスの属性として利用されるのが小粒クラス 代表的なクラスは数量・金額・日付 コラム 小粒クラスを見逃していると綺麗な設計が出来ず、関数に頼った実装になってしまいます。 氏名 住所 など、文字列で表現される小粒クラスは特に見逃されがちです。 O/Rマッパによっては、 RDBテーブル上で複数の属性となっているものを一つのクラスにマッピングできない 標準ではない自作のクラスをマッピングできない などの制限があり、小粒クラスが使いづらい場合もあります。 そういう場合であっても、マッピングを自前でコーディングすることによって小粒クラスを使うようにして下さい。保守性が格段に上がることを経験できるでしょう。 クラスにするもの tag:www.arksystems.co.jp,2014:/closeupit//5.774.000774 2014-07-30T16:00:00Z 2014-07-31T02:24:33Z 役割ではなく、それを実行するための状態を考える クラスを定義する際にはユー... ウェブマスター 役割ではなく、それを実行するための状態を考える クラスを定義する際にはユーザからの業務要件が基になりますが、それはシステムあるいはプログラム、つまりオブジェクト指向で言えばクラスの役割を規定しているだけです。役割を規定しただけではモノを決められないことは前述の通りです。 私たち開発者は、その役割を実現出来るモノはどういう状態であるべきかを考える必要があります。なぜならば、 状態はデータ構造によって規定される からです。 繰り返しますが、その内部の状態がどうなっているかはクラスを利用する側からはあまり重要ではありません。「何をしてくれるのか?」が利用する側からは重要だからです。 しかしクラスの設計者は、 それをするためにはどういうデータが必要か? を考えて決める必要があります。 クラスには境界がある クラスを設計する際に必要なのはデータ構造を考えることです。データ構造というのは「ひとかたまりとして扱いたい情報」です。 AクラスとBクラスを異なるクラスとして定義するということは、Aの情報のかたまりとBの情報のかたまりとの間に境界があるということです。境界がなければ同じクラスでも構わないからです。境界線を引いた上で、境界線の内側にある情報(属性)同士が同じクラスとして扱われるべきです。 処理に着目してクラスを定義すると、上記のような境界線は見つかりません。2つの処理がある場合、それを1つのクラスにしても2つに分けたとしてもいずれでも実装出来てきてしまいます。2つの処理に明確な境界線は引けないからです。 しかしデータ構造の場合は人が認識出来る境界線が上記の図のように必ずあります。 クラスにしてはいけないもの 実際の設計をする上でクラスを考える際、「何をクラスにすべきか」よりも「何をクラスにしてはいけないのか」を考える方が近道です。クラスにしてはいけないのは次のようなものです。 ファイル送信クラス データ受信クラス ログ制御クラス メッセージ表示クラス 従業員管理クラス 全てに共通するのが動詞となる名詞が付けられていることです。「送信する」「受信する」「制御する」「表示する」「管理する」です。 動詞になる名詞を持つということはすなわち処理に着目して定義されています。つまりそれは処理を共通化しようとしてクラス化されており、誤っています。オブジェクト指向において共通化すべきなのは処理ではなくデータ構造なのです。 上記の延長線上として、英語の動詞に'er'を付けた名前のクラスも誤りであることが多々あります。 Controller Sender Receiver これらは動詞を無理矢理名詞にしただけで本質は上記と変わりません。そしてこれらは「役割」を表現することがほとんどで、Interfaceとして定義されるべきものを多く含んでいます。 物と結果がクラス それではクラスにすべきものは何でしょうか? システム要件の中に出てくる、 物 結果 の2種類です。 【物クラス】 この種類に分類されるクラスは、実際に存在する物として人間が認識出来るものです。データベースのマスタデータとして分類されるものを多く含みます。 商品 組織 顧客 発注先会社 通貨 日付 氏名 画面上の座標 【結果クラス】 この種類に分類されるクラスは、人間やシステムが行った結果を記録したものです。商用のシステムの場合は「取引」の結果が中心となります。紙の伝票として書いてあったものは全てこの種類のクラスです。その他に、システムが行った通信の結果を記録したもの(ログ)などもこれに分類されます。 データベースのトランザクションデータとして分類されるものを多く含みます。 受注伝票 発注伝票 経理仕訳 アクセスログ 金額 受注伝票で考えてみる 商品を受注した時の伝票を考えてみます(受注時のオブジェクト図)。 上記図中のオブジェクトをまとめると次のクラス図が考えられます。 このクラス図中では、 金額クラスは商品単価の金額値を持つクラス 商品単価は「ある特定の日の商品の売り値(金額)」として定義される 日付クラスは、受注伝票クラスでも商品単価クラスでも利用される と定義しています。 ところがこのクラス図には問題となる部分があります。商品が受注個数を持つようになっていますが、受注個数は受注のたびに変わるため、商品の一部としてこれを持つのは無理です。正しくは、 「商品/受注個数/商品単価」の組合せとなる結果クラスが必要 です。受注明細と一般的に言われるものです。これを訂正したのが次のクラス図です。 上記図での注意点は商品単価です。商品単価は、 商品 日付 の2つの要素によって決まりますが、このモデルでは、 受注伝票が持つ日付 受注明細が持つ商品 によって商品単価を特定します。 アプリケーションクラス 上記の「物」と「結果」に加えて、アプリケーションプログラムそのものを具現化するクラスが必要です。次の3つがあります。 レイアウトクラス プログラムクラス メインクラス 【レイアウトクラス】 画面レイアウトクラス 帳票レイアウトクラス 画面や帳票のレイアウトを持つクラスです。Webアプリケーションの場合、画面レイアウトを専用に持つクラスがプラットフォームごとに用意されていることがあります。例えばJSPやASPです。 帳票レイアウトは出力メディアが様々なのでその時々で異なります。例えばExcelやPDF用のクラスを作成する必要があります。 これらのクラスは「情報の配置図(レイアウト)」であり、「物クラス」の性質を持ちます。 【プログラムクラス】 画面プログラムクラス 帳票プログラムクラス バッチプログラムクラス 画面のボタンが押された時や帳票プログラムが起動された時に、どの物クラスや結果クラスを扱うのかという情報を持っているクラスです。 例えば入荷実績登録画面上で登録ボタンが押されると、 入荷実績登録プログラムクラスの登録メソッドが呼ばれる 画面レイアウトクラスが持つ入力情報を受け取る 入荷商品ごとに在庫商品の数量を増加させる 入荷予定を消し込む というふうに、画面の情報を基に複数の結果クラスの状態を変えていく動きをします。 プログラムクラスはプログラム自身を具現化したものなので、 プログラムID プログラム名称 などの属性を持たせます。 【メインメクラス】 アプリケーションが起動される入り口のクラスです。このクラスはメイン関数(メソッド)を持っているだけです。 メインは関数にならざるを得ないため、この中での処理はログ出力やエラー時の対応など最低限にとどめ、プログラムクラスのインスタンスに早く委譲するようにすべきです。 メインクラスはアプリケーションプログラム個別に作成する場合と汎用的に一つだけ持つ場合があり得ます。 アプリケーションのクラス構成 上記で説明したクラスを受注伝票登録画面のアプリケーションとして仕上げる場合、次のような構成になります。 まとめ 中心となるクラスは「物」と「結果」 動詞のクラスを作ってはいけない クラスを抽出する前にオブジェクト図でまず考えてみる コラム 「処理がクラスだ」と思っている開発者が大勢います。そう思っていなくても結果的にそういう実装になっている人も多いのです。 クラスを設計する際は、 どんな情報が必要か? どんな状態を持つ必要があるか? だけをひたすら追求するようにしてみて下さい。あとはそれらの組み合わせ方(関連)を考えるだけです。 それが出来るようになればきれいな設計に自然に近づきます。 責務はクラスではない tag:www.arksystems.co.jp,2014:/closeupit//5.759.000759 2014-06-23T17:00:00Z 2014-06-24T02:25:34Z 責務でクラスは決まらない 「責務」によってクラスが定義されるといくつかの解... ウェブマスター 責務でクラスは決まらない 「責務」によってクラスが定義されるといくつかの解説書には書いてありますが、これは誤りだと私は考えています。誤りが言い過ぎだとしても、大きな誤解を与えています。 責務という言葉を言い換えると しなければならないこと 出来なければならないこと となります。クラスを利用する側から見るとそのクラスが何を出来るか(してくれるか)は確かに重要です。 しかしそれはクラスを使う側から見た場合であって、設計する人はそれだけではクラスを定義出来ません。一つ例を出します。 「ジュース売り」という責務 「ジュース売り」という責務を考えてみます。 ジュース売りがしなければならないのは次のような事です。 注文を聞く 金額を伝える お金をもらう 品物とお釣りを渡す では、この責務を請け負えるモノは何が考えられるでしょうか? ジュース売りを請け負えるモノ 人間 自動販売機 ジュース売りを請け負えるモノとして少なくともこの2つが考えられます。将来的には「ロボット」も入ってくるかもしれません。 ジュースを買いたい購買者側から見れば、それが人間だろうが自販機だろうが要件は満たされます。つまり責務が決まったからと言ってそのモノまでは決められないのです。 これは当然で、「何が出来るのか」というのは処理の規定であって、データ構造、つまりそのモノの状態は関係無いからです。 Javaなどでは「Interface」として定義するのが責務です。Interfaceはメソッドを規定するだけで中身は実装者任せです。まさに、「責務は決めるがモノは決めない」という機能です。ちなみに私は Interfaceのことを役割と呼ぶ ようにしています。このページでは責務と役割を同義として書いています。 責務で考えると誤ったクラスが出てくる例 従業員を考えてみます。業務システムを設計する際に「従業員クラス」というのは良く出て来ます。 また、スポーツジムの管理システムを設計すると「ジムの会員クラス」もあります。 これらをある1人の人の視点から見るとクラス図は次のようになります。 継承を使ってオブジェクト指向っぽい設計図になったように見えますが、果たして本当にそうでしょうか? 次の図で考えてみます。 人間を継承する形で従業員とジムの会員を定義すると「食事を採る」という処理が継承されますが、従業員とジムの会員の振る舞いとして考えた場合これは不要です。このような矛盾がなぜ起きたのでしょうか? よく考えてみるとこれらの中でモノと呼べるのは人間だけです。従業員やジムの会員は、その人がその時その時に負っている役割に過ぎません。責務と言い換えてもいいでしょう。会社の中で負っているのが従業員という役割、ジムに行けば会員という役割に変わりますが、実体としては同じ人です。 これを正しいクラス図にすると次になります。 人間の役割としての従業員とジムの会員をinterfaceとして定義します。それぞれの役割を負っている時だけその役割に必要な振る舞いのみを外部から利用出来ます。これで矛盾は解消されます。 人事システムを設計する際に「ジムの会員」という役割を考慮する必要は現実にはないと思います。人事システムから見れば「人間=従業員」という構図は成り立つので、従業員をクラスとして定義することは人事システム設計上正しいと思います。 しかしそれはシステム設計上便宜的に行っている省略であることを忘れてはいけません。役割(責務)をクラスと勘違いした結果の上記のような誤った設計/実装を大変多く見かけます。 責務をもう少し掘り下げてみる 責務というのは英語の'responsibility'の訳です。通常の訳では「責任」です。 オブジェクト(またはクラス)が持つ責務として世間で見かける解説には、 業務に必要な処理のこと オブジェクトが持っている情報を通知すること と書いてあります。 ジュース売りの例の中で業務に必要な処理というのは「注文を聞く」などです。これはその通りそのクラスが負うべき責務です。 「オブジェクトが持っている情報を通知すること」は内部にデータ構造がないと不可能なので、データ構造に依存するかのような説明に見えます。しかし、情報を通知するというのはどんなオブジェクトにも必要な処理であって、特定の役割とは無関係です。これをオブジェクトの責務と呼ぶのは勘違いだと私は考えます。オブジェクト指向においてオブジェクトに与えられている機能と言うべきでしょう。 責務というのはそのクラスがシステムの中で果たすべき役割のことであって、仕組みとして備わっている機能のことを呼ぶべきではないと言うのが私の考えです。責務はやはり処理を規定するだけでデータ構造には依存しないのです。 単一責務の原則はおかしい 1クラス1責務という原則が提唱されていますが、上記の人間クラスの例を考えてもこれはおかしいのではないかと考えています。 私が読んだサイトには次のように書いてあります。www.oodesign.com - Single Responsibility Principle(別ウィンドウで開きます) This principle states that if we have 2 reasons to change for a class,we have to split the functionality in two classes. Each class will handle only one responsibility and on future if we need to make one change we are going to make it in the class which handle it. 「クラスを変更する理由が2つある場合は機能を2つのクラスに分割すべき」というのがこの原則です。 それぞれのクラスは1つだけの責任(責務)を負い、将来変更が必要になった時にその責任を負うクラス内で変更を行います。 前々項で書いたように「人間」は複数の役割を負うことがあります。それと同じようにシステム設計の中で見つけたクラスが複数の役割を負うことはよくあることです。 ある一つの役割が負うべき機能や処理が増えた場合、その役割(interface)だけを変更した上で、役割を請け負っている実体つまりクラスを変更します。同じクラスが別の役割も負っている場合はその役割の変更に応じて再度修正すべきです。 例えば受注伝票と発注伝票というクラスを考える場合、それぞれが明細の一覧を持ちます。「明細の一覧を扱う」という業務的な役割です。この役割には、 明細の合計金額を返す 明細の件数を返す などの処理が考えられます。 一方で、これらの伝票をRDBに保存したいような場合は「RDBを読み書きする」というシステム的な役割も負います。 伝票をRDBに書く 伝票をRDBから読む などが考えられます。 「明細の一覧を扱う」・「RDBを読み書きする」それぞれの役割において変更が発生すれば、それぞれに対する修正が必要となるのは自明です。 この例においてRDBを読み書きする役割を別のクラスに分けてしまうとそのクラスはデータを持たない処理だけのクラスになってしまい、 データ構造としての伝票クラス RDBを読み書きする処理のクラス の2つに結局分割されることになり、データ構造と処理を一体化させるためのクラスという原理から外れてしまいます。 ただし、同じ伝票クラスの情報を「テキストファイルにも出力したい」という新たな要件が出てくると新たなメソッドを伝票クラスに実装する必要がまた出て来ます。その次はネットワークに出力したい等も考えられ、出力媒体が出てくるたびに処理を追加する必要が出て来ます。この問題への対応は別ページで書きます。 みんななぜ間違うのか? 世間ではクラスと思われているものが実は誤りである。なぜそうなってしまうのでしょうか? クラスを設計する際、その基となるのは顧客からの要求です。要件と呼ばれます。この要件は、 システムでxxxがしたい システムを使ってmmmが出来るようになりたい という実務処理の記述です。裏返すと、 システムはxxxが出来なければならない システムはmmmというサービスを提供しなければならない となります。つまりシステムの役割・責務を列挙したものです。 役割というのは上で書いたように処理に過ぎません。クラスとして必要なデータ構造はそれだけでは出て来ません。 しかし、オブジェクト指向に不慣れな設計者・実装者はその要件(処理)だけを頼りに開発してしまいます。 データ構造を考えずに処理だけを追いかけてしまう のです。これが誤りの根本です。 まとめ 役割(責務)をクラスにしてはいけない 役割はInterfaceにする 要件だけに着目してもクラスは見つけられない コラム 責務という言葉の解釈によってこのページで書いたことは変わってくるとは思っています。つまり責務という意味の中に データ構造を持つことを含むかどうか です。 データ構造を持つこと自体はクラスとしての機能であって特定の目的のために定義したものではありません。クラスを決めるための責務に機能自体を含めるのは私の感覚にはハマらないのです。 「責務」あるいは「責任」以外の意味がresponsibilityという英語にひょっとしてあるのかも知れません。そのモノの状態を含むというニュアンスです。私は英語ネイティブではないため判断付きませんが、日常会話で使う場合に「責任」以上の意味で聞こえた経験はありません。仮にモノの状態を含むのだとしても、責務や役割を規定することに頼らずにクラスを設計していくことが出来ます。 一方で、 責務を全うするためにはデータ構造が必要となる とは言えます。しかしだからと言って、責務を考えた結果だけでデータ構造を導き出せるとは私の経験上思えません。 責務という言葉が一人歩きして、データ構造と処理を結局分離してしまっているシステムを多数見ています。「すること」に着目してしまっているからです。これは過去の手法への回帰にはなっても、進歩には決してならないと感じています。 プログラムの開発手法は次のように変遷してきたと思います。この遷移は歴史的検証をしたものではありませんが、大まかにはこうではないでしょうか。 手続き型 構造化 データ中心主義 オブジェクト指向 データ中心主義の時代を経てオブジェクト指向が生まれてきたのに、データ中心で設計するケースが世間では未だに少ないのは非常に残念です。 「することに着目せずに」クラスを見つけることは出来ます。そのことを次ページ以降に書きたいと思います。 クラスとはデータ構造 tag:www.arksystems.co.jp,2014:/closeupit//5.758.000758 2014-06-23T16:00:00Z 2014-06-24T03:06:30Z クラスを見つけるのがオブジェクト指向設計 オブジェクト指向にはクラスが必要... ウェブマスター クラスを見つけるのがオブジェクト指向設計 オブジェクト指向にはクラスが必要です。オブジェクト指向的に設計しようとすることはクラスを見つけることに他なりません。 AクラスとBクラスが異なるクラスとして必要な理由は何? クラスを見つけるというのはつまりクラスを定義することですが、では、AクラスとBクラスという2つの異なるクラスが必要になる時の理由は何でしょうか? 処理が異なる 責務が異なる データ構造が異なる 正解は「3.データ構造が異なる」です。 クラスの根本は、 データ構造(構造体)とそれを扱う処理(関数orメソッド)を一体化させたもの です。 データ構造というのは属性(プロパティ)の集まりのことで、それら属性の値を読んだり編集したりするために処理は存在します。 次の図はOracleのJava入門のページに書いてあるものです。出典: What Is an Object?(別ウィンドウで開きます) Fieldsというのがデータ構造のことで、その周りを処理(Methods)が取り巻いています。この図は、クラスを基に生成されたオブジェクトの概念を書いたものですが、オブジェクトの設計図となるクラスの定義も同様な考え方です。 この図からも解るように、 データ構造を持たないクラスはそもそもクラスとして機能しない のです。データ構造と処理の一体化というクラスの原理に反するからです。 クラスの成り立ち 手続き型の時代から、オブジェクト指向のクラスがどのようにして生まれたのかを見ていきます。 手続き型の言語においても共通するデータ構造(構造体)を最初に決めます。 次に処理(関数)を作成し、処理と処理がデータ構造を受け渡す形でロジックを組み立てます。 ところが似たような処理があちこちに意図せず作成されてしまいます。 これを避けるために処理を共通化して共通関数を作成し、共通関数のみを利用するような開発ルールを設けます。しかし仕組みとしての強制力がないため、似たような関数が複数生まれる危険性は変わりません。 この危険性を取り除いたのがオブジェクト指向でのクラスです。データ構造が共通関数を持つように仕組みとして強要します。 クラスの中核はデータ構造 上記で見たように、クラスの最初に必要なのは 共通化されたデータ構造 です。 共通関数というものを開発したことのある人は解ると思いますが、 関数(処理)を共通化するためには受け渡されるパラメータをまず共通化する 必要があります。パラメータが複数ある場合、それらをひとかたまりにまとめるとデータ構造(構造体)になります。 つまり手続き型の言語においても、 処理を共通化する前にデータ構造を共通化 していたのです。クラスにおいては、 共通化されたデータ構造が処理も持てる ようになっています。 そのクラスを基にした個体(インスタンスあるいはオブジェクト)をメモリ上に複数生成 出来ます。これがオブジェクト指向の原理です。 まとめ クラスを規定するのは処理ではなくデータ構造 コラム 「クラスとはデータ構造のこと」と社内の若手プログラマー達に教えています。 正確に言うと「データ構造と処理を一体化したもの」なんですが、クラスを見分ける際に最初にやることはデータ構造を考えることなので上記のように繰り返し言っています。 にもかかわらず彼らは処理に目が行ってしまい、手続き型の考えにすぐなってしまいます。 これが人間の本能なのかは判りませんが、プログラマーはそれに陥りやすいと思って設計者は設計する必要があるようです。つまり曖昧な言葉による表現は避け、きちんとしたクラス図を書いて依頼し、プログラマー任せの製造は避けるべきだと考えています。 またプログラマー達がコードのみで考える状態から脱却するため、早いうちからクラス図の読み書きを修得するよう指導することも重要です。 オブジェクト指向を上手に活かすために tag:www.arksystems.co.jp,2014:/closeupit//5.755.000755 2014-06-23T15:00:00Z 2016-10-12T01:59:35Z ここに書いたのは、私が設計・実装したJavaのフレームワーク開発を主に通じて... ウェブマスター ここに書いたのは、私が設計・実装したJavaのフレームワーク開発を主に通じて理解したオブジェクト指向の原理原則です。 私は単なるエンジニアであって学者や研究者ではない上に、オブジェクト指向について誰かから教わった経験も無いため、ここに書いてある内容は科学的に吟味されたものではありません。 しかし、普段の仕事の中で気付いた合理性のある内容だと考えています。オブジェクト指向言語を日常使ってはいても、オブジェクト指向そのものをみっちりと学習したことがない人にとって特に役立つ内容だと思います。 オブジェクト指向って? オブジェクト指向の最初の説明として良く出てくるのが、 隠蔽 継承 多態性 というものです。 これらはオブジェクト指向の特徴を確かに言い表していますが、オブジェクト指向的な設計や実装を初学者が理解するための役に立たないと私は考えています。少なくとも初心者にとっては百害有って一利無しです。 これから説明するオブジェクト指向に関する原則は、実際の開発現場で皆さんが実践することを目的に書いています。つまり、 開発現場で役立つオブジェクト指向の本質を解き明かす ために書きました。 オブジェクト指向設計に関してはデザインパターンという形で様々な本が書かれていますが、パターンは有用ではあるものの全部を一々覚えてはいられません。そもそもパターンというのは、オブジェクト指向の本質を理解した人が設計した有用な常套手段に名前を付けたものです。 パターンは囲碁で言う定石 です。しかし、 定石には必ず理由があり、それらの根底には共通する原理原則がある のです。 このサイトでは、オブジェクト指向設計における原理原則を記述しています。つまり、 定石を産み出すための原理原則 です。囲碁で言えば手筋(てすじ)が近いでしょうか。 これを理解してしまえばパターンのみに頼ることなく正しい設計にたどり着けると私は考えています。しかしパターンを否定しているのでは決してありません。 パターンの奥に隠れた本質(原理原則)を理解する ことが大切なのです。 オブジェクト指向の利点 オブジェクト指向で開発すると何がいいのでしょうか? 一言で言うと、 1箇所直せばみな直る です。 実はオブジェクト指向は、最初にコードを書く時の生産性は手続き型に較べて低いのです。しかし、 開発中あるいは開発後に仕様変更または仕様ミスがあって修正する 別のシステムでの部品を流用する ような場合に威力を発揮します。つまり、 保守性が高い 再利用性が高い という特徴があるのです。 そのため、 徹底したオブジェクト指向で開発されたシステムは総合的な生産性が高い と言えます。 クラス図の読み方 このサイトでは、UML(Unified Modeling Language)のクラス図(Class Diagrams)がたくさん出て来ます。ご存じない人のためにその読み方を簡単に説明します。次の3つがあります(UMLの定義ではこれ以外もあります)。 関連 実現 汎化 関連 「独立したAクラスとBクラスがお互い相手を参照する」という関係を表します。「バイク本体とタイヤ」のような関係です。タイヤはバイクの部品ですが、あるタイヤを色々なバイクに取り付けることが可能です。 このバイクの部品として使っているタイヤ このタイヤを利用しているバイク という双方向の関係が成立します。 関連を記述する場合は 多重度(カージナリティ:cardinality) を省略してはいけません。これが記述されていないと、そのモデルが扱う業務の目的を理解できないからです。 実現 「Mインタフェースの振る舞いをCクラスが具体化(実装)している」という関係を表します。振る舞いの型(呼び出し名、パラメータ、戻り値)だけをインタフェースでは定義し、その具体化(実装)はCクラス側に任せます。 インタフェースは「役割」 と理解して下さい。 例えば自動車と飛行機の役割は「乗り物」です。この場合、乗り物インタフェースを実現(実装)する形で自動車クラスと飛行機クラスを設計します。 汎化 「EとFの性質の共通部分を抜き出してDとした」関係のことです。例えば「ワゴン車とセダンを自動車と呼ぶ」という関係です。「継承」とも呼ばれます。 ワゴン車は自動車を継承している。 また「is a の関係」とも言われます。 E is a D. ワゴン車は自動車である。(自動車として扱える) 集約は使わない UMLの産みの親「Three Amigos」の一人であるジム・ランボーは、 集約はプラセボ(プラシーボ)効果である と言いました。つまりその効果は「気のせい」で、有用性は無いという意味です。 最近まで私は関連を使わずに集約のみで書いていたのですが、色々迷って考えた結果この逆の結論に至りました。モデルを表現する際、集約は紛らわしさを助長します。 集約は「has a」の関係と言われ、 AクラスがBクラスを持つのか? BクラスがAクラスを持つのか? によって菱形を付けるクラスが入れ換わりますが、関連があるクラス同士は双方向で参照するため、 どちらも相手を持てる(持つ必要がある) という結果になるのです。そのため集約は意味を成しません。 このサイトを読む上での注意点 このサイトに書いてある内容には、皆さんのこれまでの常識をくつがえすものが多々あります。今までの常識にとらわれずに読んで下さい。可能ならば頭を白紙の状態にして、書いてある内容を純粋に吟味してみて下さい。皆さんの開発にきっと役立つはずです。 それでは次ページから、「本には書いてないオブジェクト指向」の始まりです。 DB2PLI8プログラム tag:www.arksystems.co.jp,2012:/closeupit//5.642.000642 2012-09-18T19:00:00Z 2012-09-19T01:33:09Z このプログラムは、DB2の製品の一部として提供されているものではありませんが... ウェブマスター このプログラムは、DB2の製品の一部として提供されているものではありませんが、IBM社のサイトからダウンロードして使用することができるプログラムです。 このプログラムは、DB2 V8、およびV9に対応していますが、V10には対応していません(*注)。 *注 V10(およびV9)では、DB2提供のプログラムDSNADMSB、およびストアード・プロシージャーADMIN_INFO_SQLを使用 して同等の機能を実現することができます。APAR PM39871 の適用が前提となります。 このプログラムを使用すると、DB2カタログ上のアクセスパス関連の情報を参照して、その情報を再現するSQLステートメント(DB2カタログ表の更新ステートメント)を生成することができます。 例えば、RUNSTATS(統計情報の更新)を実施する前に、DB2PLI8を使用して、現状のDB2カタログ情報を再現するSQLステートメントを生成、保管しておけば、統計情報の更新の結果、アクセスパスが変化し、パフォーマンス劣化が発生した場合などに、DB2カタログの統計情報を元に戻すことができます。 また、本番環境のDB2カタログの統計情報を再現するSQLステートメントをDB2PLI8で生成し、そのSQLを検証環境で実行することにより、本番環境の統計情報を検証環境に移植するといった使い方も考えられます。 実行例 DSN SYSTEM(DSN1) RUN PROGRAM(DB2PLI8) PLAN(DB2PLI8) LIB('DSN910.RUNLIB.LOAD') -        PARM('/DSN8910.EMP') END 出力例(生成されるSQLステートメント) * DSN8910.EMP  *** From DB2PLI8 9.1 (AUG 21, 2009)             ***      * CATALOG IS V9  OUTPUT IS FOR THIS LEVEL                 ***           * CATALOG IS V9  New function mode                                      UPDATE SYSIBM.SYSTABLES SET                                                     CARDF =  +4.200000000000000E+01,                                       NPAGES =           2,                                               PCTROWCOMP =           0,                                                AVGROWLEN =          81,                                                  NPAGESF =  +2.000000000000000E+00,                                       SPACEF =  +4.512000000000000E+03,                                     PCTPAGES =           3,                                                STATSTIME = '2011-05-30-19.55.24.652542'                             WHERE CREATOR = 'DSN8910' AND NAME = 'EMP';                           …(以下省略)…     以上、DB2 for z/OSのアクセスパス管理機能に関するトピックスを、「性能、パフォーマンスの最大化、最適化」の観点からいくつか紹介いたしましたがいかがでしたでしょうか? これらの機能を使いこなすためには運用上のしくみと工夫が必要となります。 少し宣伝になりますが、アークシステムはDB2を知り尽くしたスペシャリストを擁していますので、そのノウハウを提供する形で性能問題に限らず、運用上のさまざまな課題を解決していくことが可能です。ぜひお気軽にご相談いただければと思います。 BIND/REBIND:APCOMPAREオプション tag:www.arksystems.co.jp,2012:/closeupit//5.641.000641 2012-09-18T18:00:00Z 2012-09-19T01:27:31Z パッケージのBIND/REBIND時に、新たに選択されるアクセスパスと以前の... ウェブマスター パッケージのBIND/REBIND時に、新たに選択されるアクセスパスと以前のアクセスパスの比較を実施する機能です。 この機能は、V10から提供されています。 比較の結果、アクセスパスが完全に一致する場合のみパッケージを生成し、一部でも変化する場合は新たなパッケージの生成を行わないオプション(ERROR)と、アクセスパスが変化した場合でもパッケージの生成を行うオプション(WARN)があります。 比較の結果(アクセスパスが一致したステートメントの数、変化したステートメントの数)は、メッセージ出力されます。 前述のEXPLAIN(ONLY)オプションと同時に使用することで、実際にパッケージの生成を行うことなく、選択されたアクセスパスの情報(アクセスパスの変化の有無)を確認することもできます。 APCOMPARE(NONE/WARN/ERROR) NONE アクセスパスの比較は行われません。 WARN アクセスパス比較の結果、アクセスパスが変化する場合でも新たなパッケージを生成します(RC=4)。 ERROR アクセスパス比較の結果、アクセスパスが一部でも変化する場合は、新たなパッケージの生成は行われません。BIND/REBINDはエラーになります。 例 REBIND PACKAGE (COLL1.PROG01) EXPLAIN(ONLY) APCOMPARE(WARN) BIND/REBIND:APCOMPAREオプションの使用に関する注意事項 この機能が適用できるのは、V9以降のパッケージです。 この機能を使用するためには、以前に、パッケージが、V9以降のDB2の下でBIND、あるいはREBINDされている必要があります。 BIND/REBIND:APREUSEオプション tag:www.arksystems.co.jp,2012:/closeupit//5.640.000640 2012-09-18T17:00:00Z 2012-09-19T01:23:02Z パッケージのBIND/REBIND時に、以前のアクセスパスを再利用することを... ウェブマスター パッケージのBIND/REBIND時に、以前のアクセスパスを再利用することを指示できる機能です。 この機能は、V10から提供されています。 APREUSEオプションを使用することにより、既存のアクセスパスが利用可能な場合は、アクセスパスを変更することなく、新たなパッケージを生成する(BIND/REBINDする)ことができるようになります。 オプティマイゼーション・ヒント機能を使用する際に必要となる、PLAN_TABLEへのヒント情報の登録といった事前準備作業は必要ありません。コマンドのオプション指定のみで、この機能を使用することができます。 APREUSE(NONE/ERROR) NONE アクセスパスの再利用は行われません。 ERROR アクセスパスの再利用が試みられ、利用可能なアクセスパスがある場合には、アクセスパスが再利用され、パッケージが生成されます。アクセスパスの再利用ができないSQLステートメントがパッケージ内に含まれる場合、パッケージの生成は行われません。BIND/REBINDは、エラーになります。 例 REBIND PACKAGE (COLL1.PROG01) APREUSE(ERROR) BIND/REBIND:APREUSEオプションの使用に関する注意事項 この機能が適用できるのは、V9以降のパッケージです。 この機能を使用するためには、以前に、パッケージが、V9以降のDB2の下でBIND、あるいはREBINDされている必要があります。 BIND/REBIND:EXPLAIN(ONLY)オプション tag:www.arksystems.co.jp,2012:/closeupit//5.639.000639 2012-09-18T16:00:00Z 2012-09-19T01:13:29Z パッケージのBIND/REBIND時に、パッケージの生成は実施せずに(現行の... ウェブマスター パッケージのBIND/REBIND時に、パッケージの生成は実施せずに(現行のパッケージは変更せずに)、新たに選択されるアクセスパス情報をPLAN_TABLEに書き出すオプションです。 この機能は、V10から提供されています。 V9までのEXPLAINオプションは、あくまでもBIND/REBIND処理(パッケージの生成処理)の追加処理として、選択されたアクセスパス情報をPLAN_TABLEに書き出す機能であり、稼動中のパッケージに影響をおよぼすことなく(アクセスパスが変更される場合は、その変更がパッケージに適用されることなく)、新たに選択されるアクセスパスの情報を取得することはできませんでした。 V9までは、稼動中のパッケージに影響をおよぼすことなく新たに選択されるアクセスパスを確認するためには、プログラム(パッケージ)に含まれるSQLステートメントを抽出し、個別に、手動で、EXPLAINを実施しなければならず、そのためには多くのワークロードが必要でした。 この機能を使用することにより、稼動中のパッケージに影響をおよぼすことなく(かつ多くのワークロードを費やすことなく)、新たに選択されるアクセスパスの情報を取得する(シミュレーションを実施する)ことが可能になります。 例 REBIND PACKAGE (COLL1.PROG01) EXPLAIN(ONLY) BIND/REBIND:EXPLAIN(ONLY)オプションの使用に関する注意事項 この機能が使用できるのは、パッケージに対してのみであり、プランについては使用することができません。 BIND/REBIND PLANでは、このオプションを使用することはできません。 EXPLAINステートメント:PACKAGEオプション tag:www.arksystems.co.jp,2012:/closeupit//5.628.000628 2012-08-07T17:00:00Z 2012-08-08T02:07:49Z 現在稼動しているパッケージのアクセスパス情報をPLAN_TABLEに書き出す... ウェブマスター 現在稼動しているパッケージのアクセスパス情報をPLAN_TABLEに書き出す機能です。 この機能は、V10から提供されています。 パッケージのBIND/REBIND時に選択されたアクセスパス情報を保管していない場合、V9までは、現在稼動しているパッケージのアクセスパス情報を知る方法がありませんでした。 この機能を使用することにより、EXPLAIN情報(アクセスパス情報)の取得や保管が実施されていない環境においても、PTF適用などにともなうREBINDによるアクセスパスの変化を確認、検証することが可能になります。また、この機能によって出力されるアクセスパス情報をオプティマイゼーション・ヒント機能使用時の入力(ヒント)とするといった利用方法も考えられます。 例 EXPLAIN PACKAGE COLLECTION 'COLL1' PACKAGE 'PROG01' ; EXPLAINステートメント:PACKAGEオプションの使用に関する注意事項 この機能が適用できるのは、V9以降のパッケージです。 この機能を使用するためには、V9以降のDB2の下でパッケージのBIND、あるいはREBINDを実施しておく必要があります。