asatoの技術的な日常日記

「成長に最大の責任をもつ者は、本人であって組織ではない。自らと組織を成長させるためには何に集中すべきかを、自らに問わなければならない」  非営利組織の経営 - ピーター・ドラッカー

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

コード進化パターン:Data Manager クラスへの setter/getter の追加

「コード進化のパターン」を考えるパート何だっけ、3ぐらいか。

本ドキュメントは「Java におけるコード進化パターン」にあります。

今回追加したのは「Data Manager クラスへの setter/getter の追加」というパターン。

簡単に言えば、Data Manager micro pattern のクラスに setter/getter のメソッドを同時に追加するような進化のパターン。

Data Manager micro pattern っていうのは、Maman らが提案してる micro pattern のカタログの中の一つ(日本語版の翻訳は こちら)。

micro pattern は、デザインパターンよりも抽象度が低くて、デザインパターンと違って機械的に検出できるようなクラスや interface に対して、構造的な特徴の観点から分類したもの。たとえば、Data Manager は、getter と setter のみのメソッドから構成されるクラスのこと。他にも、public メソッドを一つだけもつクラスを表す Function Pointer など、(論文出版時点で)全部で 27 個ある。


今回の進化パターンでは、この Data Manager を対象としてその進化のパターンの一つを取り上げたい。進化は簡単で、setter/getter のメソッドを Data Manager に同時に追加するケース。以下は進化前と後のクラス図。

before:
add_getter_and_setter_to_data_manager_before.jpg


after:
add_getter_and_setter_to_data_manager_after.jpg


簡単で、単に data3 フィールドを追加という感じ。


もちろん、この進化パターンは、実例からのものを参考にしています。具体的は、次のような Status クラス:
Before:

public class Status {

private int hp = 20;
private int maxHP = 20;

private int str = 5;
private int def = 2;

private int hitRate = 100; // 命中率
private int avoidRate = 5; // 回避率

public int getHP() {
return hp;
}

public void setHP(int hp) {
this.hp = hp;

if (hp > getMaxHP()) {
this.hp = getMaxHP();
}
if (hp < 0) {
this.hp = 0;
}
}

public int getMaxHP() {
return maxHP;
}

public void setMaxHP(int maxhp) {
this.maxHP = maxhp;
}
// ... その他 getter/setter
}


追加したのは、クリティカル率を表す criticalRate フィールド。
after:

public class Status {

private int hp = 20;
private int maxHP = 20;

private int str = 5;
private int def = 2;

private int hitRate = 100; // 命中率
private int avoidRate = 5; // 回避率
private int criticalRate = 5; // クリティカル率

public int getHP() {
return hp;
}

public void setHP(int hp) {
this.hp = hp;

if (hp > getMaxHP()) {
this.hp = getMaxHP();
}
if (hp < 0) {
this.hp = 0;
}
}

public int getMaxHP() {
return maxHP;
}

public void setMaxHP(int maxhp) {
this.maxHP = maxhp;
}
// ... その他 getter/setter

public int getCriticalRate() {
return criticalRate;
}

public void setCriticalRate(int criticalRate) {
this.criticalRate = criticalRate;
}

}


ここでいくつか疑問があると思う。

(1)回避率は、avoidRate なのか。 まあこれは冗談。ただ、普通ゲームで言うところの回避率を英語でなんというか知ってる人は教えてください。

(2)setHP メソッド内のロジックの存在。これはちょっと迷うところ。このようなロジックがある場合、setter と呼んでしまっていいのかどうかという点のため。また、それに関連して Data Manager と呼んでしまっていいのか、という点。これらの点に関しては、アスペクト指向の観点も含めて別の記事で分析したいと思う。


ちなみにこの「Data Manager クラスへの setter/getter の追加」の進化パターンは、次のようなパターン階層に位置している(と考える)。
evo_relation_add_getter_and_setter_to_data_manager.jpg


「メソッドの追加」の進化パターンの全体階層は、今のところこんな感じ。
evo_relation_add_method2.jpg



話は少し変えて、この「Data Manager クラスへの setter/getter の追加」の進化パターンに至る進化のシナリオについて考えるのも、この研究の一部。つまり、この進化パターンは何の後に起こるのか。あるいは、この後にどんな進化パターンが起こるのか、という調査。

この後に何が起こるのかはまだ分析してない(or 実践で出会ってないので分析できない)けれど、前に何が起こっていたのかについての可能性は少し分かっている。候補は二つある(詳しくはリンク先を参照)。
(1)「Data Manager クラスの追加
(2)「パラメータからの Data Manager クラスの抽出

一つ目は、何も無い状態から新たに Data Manager クラスを追加するようなケース。たとえば、機能追加時に新たに追加されるようなケース。

二つ目は、リファクタリング時に、メソッドのパラメータから抽出されたクラスが Data Manager だったというケース。"メソッドのパラメータから抽出" ってのは、たとえば、メソッドのパラメータ数が多かったので、まとめて一つのクラスにする、というようなケース。


なので、とにかく「Data Manager クラスへの setter/getter の追加」が起こるためには、すでに Data Manager が存在していなければならない。上記の二つは Data Manager を導入する進化パターンの例といえる。

進化のパス(or シナリオ)を図で書くなら、次のようになる。
evo_path_add_getter_and_setter_to_data_manager_blog.jpg


しかし、僕は、「コード進化のパターンのカタログ」の中には、この図は入れないことにした。なぜか。それは、例で挙げた Status クラスがその二つのどちらにも当てはまらなかったから。

では、 Status クラスはどこから来たのか。元々は、次のようなクラスがあった。
before:

public class Suiseiseki implements Player, Animatable {
// その他のフィールド

private int lv = 1;
private int exp = 0;

private int hp = 20;
private int maxHP = 20;

// その他のメソッド
public int getHP() {
return hp;
}

public void setHP(int hp) {
this.hp = hp;

if (hp > getMaxHP()) {
this.hp = getMaxHP();
}
if (hp < 0) {
this.hp = 0;
}
}

public int getMaxHP() {
return maxHP;
}

public void setMaxHP(int maxhp) {
this.maxHP = maxhp;
}

// ... その他のメソッド。その他の getter/setter
}

そしてステータスに関係するフィールドが増えてきたので、Status クラスを作ってリファクタリングすることにした。
after:

public class Suiseiseki implements Player, Animatable {

// その他のフィールド

private int lv = 1;
private int exp = 0;

private Status status = new Status();

private int hp = 20;
private int maxHP = 20;

// その他のメソッド
public int getHP() {
return status.getHP();
}

public void setHP(int hp) {
status.setHP(hp);
}

public int getMaxHP() {
return status.getMaxHP();
}

public void setMaxHP(int maxhp) {
status.setMaxHP(maxhp);
}

// ... その他のメソッド。その他の getter/setter
}


元々のリファクタリングの目的は、Suiseiseki クラス(このクラスプレイヤーを表す)から、ステータスに関連するメソッドとフィールドを別のクラスである Status クラスに移動させることで、Suiseiki クラスと Player interface のインタフェースをシンプルにすることだった。

けれど、変更後のコードから分かるように、実際には、色々な都合のため Status クラスへフォワーディングするだけのコード変化になってしまった。


ここで再び Data Manager である Status クラスがどのようにして出現したのかを考えてみたいと思う。候補は二つあった:
(1)「Data Manager クラスの追加
(2)「パラメータからの Data Manager クラスの抽出

一番ではない。Status クラスは、既にあるクラス(Suiseiseki クラス)から抽出された。

二番目でもない。Status クラスは、既にあるクラスが持っていたフィールドとメソッドを移動させるために導入された。

これらの理由から、「Data Manager クラスへの setter/getter の追加」という進化パターンが「Data Manager クラスの追加」あるいは「パラメータからの Data Manager クラスの抽出」の後に起こるという進化のパスが存在するとは、(現状では)いえないと判断したのだった。


今回の話は以上で終わり。だけど、一つ関係することとして面白そうな調査内容を。

同じ Data Manager クラスといえども、元々どのような進化のパスを通して出現したのかによって、後の進化パスは異なるのではないか、という点。

たとえば、メソッドのパラメータから抽出された Data Manager クラスと、あるクラスのフィールドから抽出された Data Manager クラスとでは、この後に同じような進化のパスを通るのだろうか?

これに回答するには、エンピリカルな実験が必要だろうけど、直感としては、メソッドのパラメータから抽出されたような Data Manager クラスは、その後「Data Manager クラスへの setter/getter の追加」みたいな進化のパスを通りにくいんじゃないかと思う。

それが正しいとしたら、次の疑問は、では、それはなぜか、という点。


以上。
スポンサーサイト

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバックURLはこちら
http://asatohan.blog77.fc2.com/tb.php/36-1913e314
この記事にトラックバックする(FC2ブログユーザー)

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。