各モジュール間でデータを伝言ゲームのように次々と受け渡ししていかなければいけない時の対処


アンチパターン1

必要なデータをそのまま受け渡すと、後からの変更に弱くなってしまう。

例えば、以下のようなデータを考える。

firstName (文字列)
lastName (文字列)

このデータをクラスA -> B -> C -> D -> Eを伝言ゲームのように受け渡しており、各クラス内部それぞれでもこのデータを処理に利用しているとする。

もし、このデータに以下のデータを追加すると・・・

age (整数)

A, B, C, D, E全クラスで変更が必要になってしまう。

クラス数が少ない場合(アプリケーションなどの規模が小さくクラス数が少ない場合)は、あまり問題にならないかもしれない。しかし大規模・複雑なシステムでクラス数が多い場合はかなり問題となる。

システム開発中に必要なデータが変わるなど日常茶飯事とも言える昨今ではなおの事問題である。

対策

データをこのように別の「決まり事」(クラス、構造体、プロトコル、インターフェースなど各言語で色々あるだろうが)で包む。

class Person {
   firstName (文字列)
   lastName (文字列)
}

こうすると、データを受け渡す時はclass Personのみ意識すればよく、Personの中身がどんなものかは意識しなくて良い(隠蔽される)ということになる。言い換えれば、変更に対して強くなる。

例: データを追加したい時

以下のようにデータの定義に変更を加える。

class Person {
   firstName (文字列)
   lastName (文字列)
   age (整数)
}

後は、ageを利用したい箇所で変更を加えればいいだけなので、最小で二箇所のみの変更で済む。

A~Eの全クラスで変更をしなければいけなかったのとは、雲泥の差と言える。

例: データを削除したい時

以下のようにデータの定義に変更を加える。

class Person {
   firstName (文字列)
}

後は、削除したlastNameを使っていた箇所(があればそこ)で変更を加えるだけとなる。

アンチパターン2

そもそも複数のモジュール間でデータを次々と受け渡すことは、各モジュールにとって知っておかなければならない知識が一つ増えることを意味する。そのため、必要でない場合はやるべきではない。あくまで、それら全モジュールがそのデータを必要としている場合に限る。

伝言ゲームをしたくない場合、解決策としてグローバルなところに変数を作っておき、必要なクラスだけから参照するという手もあるが、グローバルな変数の多用はそれはそれで問題がある。

いわゆるコンテキストオブジェクトを導入する手もある。ソフトウェアの状態などを保有するオブジェクトを作り、必要なクラスに対してコンストラクタを経由して渡すという形式である。やはり多用すると複雑になる恐れがあることから、コンテキストオブジェクトを保有するクラスの数はメジャーなクラスのみに限るなど何らかの制約をしたほうがいい。またコンテクストを複数箇所から参照するとスレッドセーフにしなければならないという問題もある。コンテキストオブジェクトを注入する際、刻々と変化する状態への参照を渡したつもりが、値そのものを渡してしまい、後から状態が変化しているのに当初のコンテキストオブジェクトの値をずっと見てしまうというミスがたまにあるため、言語の使用を把握し「値渡し」か「参照渡し」かをきちんと区別する。

参考文献

Context Object Design Pattern – Core J2EE Patterns