asatoの技術的な日常日記

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

スポンサーサイト

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

ソースコードからソフトウェアデザインは学べるか? その1

ソースコードからソフトウェアデザインは学べるか?」

まず、この質問/疑問は、適切なものだろうか? つまり、「うどんとラーメンどちらが優れているか?」といった類の質問でないかどうか。

適切だとしたら、次に、この疑問に答えることは重要なのだろうか? もしYESなら、その理由は?


この疑問に答える上で、最初の難関は「ソフトウェアデザイン」という用語の定義だと思う。まず、ソフトウェアに限らず、工学におけるデザインとは何か? 次に、ソフトウェアにおけるデザインとは何か?


次回に続く。

コンサーン

ソフトウェアの構造が適切でなくなっていく理由は色々ある。適切でないとは、うまくモジュール化できていないとかそういうこと。

理由の一つは、技術的な要因に関するもの。たとえば、プログラミング言語の能力不足。

もう一つは、管理的な要因に関するもの。たとえば、開発者の能力不足やリリースのプレッシャーなど。


この記事では、リリースのプレッシャー、つまり、締切りの理由からとにかく動くコードを書く必要があるために、設計について考慮する時間がとれなかったとか、リファクタリングする余裕がなかった時のコードの構造について一つの例を挙げて考えたい。


以下では、適切にモジュール化されていない、という代わりに、コンサーンを適切に分離できていない、ということにする。コンサーンとは

The term concern is loosely defined to represent anything that stakeholders of a software project may want to consider as a conceptual unit. Typical concerns in a software project include features, nonfunctional requirements, design idioms, and implementation mechanisms (e.g., caching).

-----
Martin P. Robillard and Gail C. Murphy.
Representing Concerns in Source Code.
ACM Transactions on Software Engineering and Methodology, 16(1):1-38, February 2007.
http://www.cs.mcgill.ca/~martin/papers.html

つまり、コンサーンは、概念的な単位であり、ステークホルダが気にするなんでも。


さて、では、僕がこの前、適切にコンサーンを分離できていなかった例を挙げて、(すでに誰かが言及していることも含めて)何か考察してみよう。

作っているソフトウェアは、ゲーム。どんなゲームかというと、「ローグ」とか「不思議のダンジョン風」のゲーム。

以下は、プレイヤーが「足踏み」行動の入力を行ったときの処理内容を表すクラス。このクラスは、Command パターンでいうところの ConcreteCommand に対応する。


// 「足踏み」行動の時の処理内容
public class PlayerHPHealingTaskCommand implements TaskCommand {

private GameManager gameManager;

public PlayerHPHealingTaskCommand(GameManager gameManager) {
this.gameManager = gameManager;
}

public void execute() {

// プレイヤーの「満腹度」が0なら、一定の値で HP を減少
// そうでなければ、HP を増加
//
// ... コード

// 「満腹度」を減少
status.setHungry( status.getHungry() - 20 );
}
}



「足踏み」行動の基本的な仕様は、「プレイヤーの「満腹度」が0なら、一定の値で HP を減少そうでなければ、HP を増加。その後、「満腹度」を一定値で減少させる、というのもの」

コード的に色々まずいところもあるけど、一つまずいのは、「HP値を減少/増加される」というコンサーンと「満腹度を減少させる」というコンサーンがうまく分離されていない部分。
concern.png



あるいみでは、仕様書通りの振る舞いなのだけど、「満腹度を減少させる」という処理は「移動」行動の処理を行うときの、サブ処理の一つなので、「満腹度を減少させる」という処理を一つの ConcreteCommand として分離して、「足踏み」と「移動」の二つの処理で使えるように出来るのが望ましい。
concern2.png

concern3.png





考察


まず、コンサーンがうまく分離できてないと、プログラムの理解や修正が難しくなる。

次に、Command パターンを適用している構造の時、ConcreteCommand として分離されるべきコンサーンは、からまってしまう状況がある。
concern4.png


ソフトウェア進化研究 Part1:変化するもの

ソフトウェア進化について考えるシリーズ、第一回目。

テーマは「何が変化するか」。

ソフトウェア開発の過程ににおいて変化するものは色々ある。それらの変化を観察した結果や考察を、ソフトウェア開発に生かそうとするのがソフトウェア進化の研究だと思う。


何が変化するのか。

-環境:ソフトウェア開発の過程において環境が変化する。たとえば、法律などが変わるかもしれない。競合他社の製品のバージョンが変化するかもしれない。開発者の周りの技術が新しく生まれたり、技術の様々な仕様が変化するかもしれない。

通常、環境変化は、ユーザや開発者が制御できない。

-ユーザ要求:ユーザや顧客の要求は常に変化する。これは、ユーザ自身が、どんな問題を抱えており、その問題を解決するどのようなソフトウェアを望んでいるのかを、正確に理解していないことが主な要因だと思われる。

ユーザは、環境変化に影響を受ける。そのため、問題自体が変化する。

-デザイン(デザインデシジョン):開発者は、ユーザ要求を満たすようにソフトウェアをデザインする。デザインのプロセスは、意思決定(decision making)のプロセスだと考えられる。

様々な理由で、デシジョンは変化する。環境の変化、ユーザ要求の変化、開発者の設計問題に対する理解の変化など。

環境の変化は、例えば、用いてるライブラリが開発中止になったりすることなどが考えられる。あるいは、現在用いてるライブラリよりも優れたライブラリの出現などもある。

ユーザ要求の変化は、たとえば、機能追加などがある。デザインする上で重要なことの一つは、複雑さを適切にすることだと考えられる。2個の機能の時のデザインと10個の機能の時のデザインとは異なる。

設計問題に対する理解の変化は、設計問題が、ill-defined な問題 に属することに起因すると考えられる。

-プログラム:開発者のデザイン(デシジョン)の変化は、最終的には、ソースコードや設定ファイルなどのプログラム的な成果物に反映されなければならない。

-ドキュメント:バグレポートなどの文書など。

デザインプロセスとデザインデシジョン


Robert P. Smith and Steven D. Eppinger

A Predictive Model of Sequential Iteration in Engineering Design

Management Science, vol. 43, no. 8, pp. 1104-1120, August 1997

DL: http://web.mit.edu/eppinger/www/publications.html


に次のような文があったのが考えてみる(太字は僕による)。

In studying engineering design, it has been observed that a complete design process is divisible into discrete design decisions (Marples 1961, von Hippel 1990). Each decision is made in consideration of previous decisions and is subject to change based on later results.


デザインプロセスは、デザインデシジョンに分割できる、ってところだけど、図で描くとこんな感じか。


decision.png



ソフトウェアデザインにおけるデザインデシジョンとは何か。Cai が、Constraint Network と呼ばれる形式的なモデルを用いて説明しているので、それを見てみよう。

Yuanfang Cai

Modularity in Design: Formal Modeling and Automated Analysis

PhD Dissertation.

DL: http://www.cs.virginia.edu/~yc7a/Publications.htm


CN は、設計次元や外部状況をモデル化するための形式的なモデル。次のような要素から成る:
-変数:デザインデシジョンが行われる次元を表現
-値:デザインデシジョンを表す
-ドメイン:値の集合から成る
-論理的制約:要求される関係をモデル化

論文でも用いられているけど、例として行列クラスを考えてみる。このクラスのデータ構造は、クライアントがどうやって使うかに依存する。array は、密行列(dense matrix)に有効。linked list は、疎行列(sparse matrix)に有効。また、このクラスのメソッドの実装するアルゴリズムは、データ構造に対応しなければならない。

ここで、データ構造やアルゴリズムを設計次元とし、クライアントの要求を、外部状況として扱う。

Constraint Network を使って、この行列クラスの設計をモデル化するとこうなる。

scalar matrix: (dense, sparse)
scalar data_structure: (list_ds, array_ds, other);
scalar algorithm: (list_alg, array_alg, other);
data_structure = array_ds => matrix = dense;
data_structure = list_ds => matrix = sparse
algorithm = array_alg => data_structure = array_ds
algorithm = list_alg => data_structure = list_ds

cn.png


他にも例えば、Observer パターンを CN で表すとこんな感じ(より詳細なやつは論文を参照)。

scalar policy_notify: (push, pull) // 通知ポリシー。
scalar subject_to_observer_map: (hashtable, other); // どうやって subject と observer をマップするか。



最初のテーマとの関係性を何か言うつもりだったのだけど、発散してしまった感がある。

一つ考察するなら、デザインプロセスとは、CN を構築し、更新し、デシジョンを行うことを繰り返すプロセスといえるかもしれない。問題解決(=デザイン)のプロセスの初めから、設計空間を完全にモデル化できるわけでもないし、途中で問題が変われば、つまり、要求変化があれば、CN を更新する必要がある。

ill-defined problems

更新履歴:
2008.01.05:原文を載せました。誤字・脱字を直しました。
2007.12.20:関連記事のリンクを載せました。
2007.12.18:誤字・脱字を直しました。


Nigel Cross

Engineering Design Methods: Strategies for Product Design (ペーパーバック)

http://www.amazon.co.jp/dp/0471872504/


ill-defined problem の特徴がうまくまとめられてそうだったのでメモ。

設計者が取り組む問題は、ill-defined とか ill-structured とかに見なされるらしい。ill-defined とか ill-structured とかの反対の問題は、チェスとかクロスワードとかで、well-defined とか well-structured とかの問題。

well-defined な問題の特徴は、明確なゴールがあったり、多くの場合には正しい一つの問題があったり、答えを出す手順が知られていたり、とからしい。

一方、ill-defined な問題の特徴はこんな感じらしい(以下、微妙な翻訳)。
-問題に関する決定的な(definitive)定式化はない:問題が最初に設定されたとき、ゴールは不明確で、制約や基準の多くは不明である。問題の文脈は、複雑でありちらかっており、良く理解されていない。問題解決のプロセスにおいて、問題の一時的な定式化はフィックスされるかもしれないが、不安定であり、より多くの情報が利用可能になるにつれて変化する。

-問題に関するどんな定式化も非一貫性を含みうる:問題は、内部的に一貫していることは多くない。多くのコンフリクトや非一貫性は、解において解決されなければならない。しばしば、非一貫性は、問題解決のプロセスにおいてのみ現れる。

-問題の定式化は、解決策依存:問題を定式化する方法は、解決する方法に依存する。暗黙・明示的に解のコンセプトを参照することなしに問題を定式化することは困難である。

-解決策の提案は、問題理解の手段の一つである:問題に関する多くの仮定や、不確実性は、解のコンセプトを提案することのみで明らかになる。多くの制約や基準は、解決案を評価することの結果として現れる。

-問題に対する決定的な解決策はない:異なる解決策は、初期の問題に対する同等に妥当な反応となりうる。解に対する客観的な true-or-false の評価はなく、解は、良いか悪い、適切か適切でないかで評価される。


以下原文。
There is no definitive formulation of the problem: When the problem is initially set, the goals are usually vague, and many constraints and criteria are unkhown. The problem context is often complex and messy, and poorly understood. In the course of problem-solving, temporary formulations of the problem may be fixed, but these are unstable and can change as more information becomes available.

Any problem formulation may embody inconsistencies: The problem is unlikely to be internally consistent; many conflicts and inconsistencies have to be resolved in the solution. Often, inconsistencies emerge only in the process of problem-solving.

Formulations of the problem are solution-dependent: Ways of formulating the problem are dependent upon ways of solving it; it is difficult to formulate a problem statement without implicitly or explicitly referring to a solution concept. The way the solution is conceived influences the way the problem is conceived.

Proposing solutions is a means of understanding the problem: Many assumptions about the problem, and specific areas of uncertainty can be exposed only by proposing solution concepts. Many constraints and criteria emerge as a result of evaluating solution proposals.

There is no difinitive solution to the problem: Different solutions can be equally valid responses to the initial problem. There is no objective true-or-false evaluation of a solution; but solutions are assessed as good or bad, appropriate or inappropriate.

ill.png



関連記事:
本・デザイン:Sketches of Thought

製品進化とダーウィンプロセス

ほとんどメモ。


製品進化を説明するためにダーウィンの生物進化のプロセスをアナロジーとして用いることの問題点が以下の論文で軽く載ってた。

Wissmann, L., Yassine, A.,

"Discrete Design Adaptation within a Dynamic Environment,"

PDRL working paper # PDL-2005-03.

DL: http://www.iese.uiuc.edu/pdlab/Publication.htm


この論文では、ダーウィンプロセスは次の図で表されてた。
dawin.png


製品進化を説明するためにダーウィンの生物進化のプロセスをアナロジーとして用いることの問題点は:

(1)ダーウィン進化では、アダプティブエージェント(製品設計者)を用いることなく、変種のランダム生成のみを当てにしている。

(2)経済システムでは、製品の適合性(fitness)は、セレクティブエージェント(顧客)によって決まる

(3)ダーウィンによる製品進化では、顧客のニーズを考慮していない。これは、多くのマーケティング理論と一致しない。マーケティング理論では、大きなゴールとして顧客のニーズを満たそうとする。

(4)価格は、ランダムでない。
(5)広告メッセージは、ランダムでない。
(6)流通戦略(Distribution Strategy)は、ランダムでない。

ってなことから、ダーウィンの話を製品進化に結びつけるのはよろしくないんじゃない? ってことらしい。


もちろん、僕が興味あるのはソフトウェア進化だから、安易にダーウィンな話を持ち出すのは良くないかも、ってことを学んだのであった。

変更容易に関する研究

変更が容易 or 進化が容易なソフトウェアを開発するための方法論や原理原則を見つけるのが僕の今の研究テーマ。

特に、要求変化、それに伴うデザイン変化、そして変化のパターンの観点から攻めていきたい。


vibha sazawal さんが、変更容易(ease of change)を取り扱っている研究を、大きく4つに分類している(「6章:関連研究」)ので、それを軽くまとめたい。

Connecting Software Design Principles to Source Code for Improved Ease of Change

PhD Dissertation, December 2005.

DL: http://www.cs.umd.edu/~vibha/


一つ目のカテゴリは、変更を容易にするためのメカニズムについての研究。彼女によれば、言語、デザインパターン、モデルなどがこのカテゴリに含まれる。

二つ目は、プログラマが設計問題を特定したり、再構築を計画するのを支援するツール。


三つ目は、コードのビューを提示するようなツールに関する研究。

四つ目は、モデル駆動開発のツールに関する研究。

eoc_category.png


一つ目のカテゴリのアプローチの基本的な前提は、変更容易なソフトウェアを表現するのを容易にするメカニズムや表現を作ることができる、というもの、らしい。

でも、単にメカニズムが存在するだけでは変更容易のためには不十分。メカニズムは、適切に使われなければならない。


第一のカテゴリを、彼女は、さらに3つのセクションで議論している。その三つってのは:
(1)情報隠蔽のための言語や言語のようなメカニズム
(2)疎結合low coupling)のための言語や言語のようなメカニズム
(3)デザインパターン

一つ目の情報隠蔽のセクションでは、以下が議論の対象になってる。
-オブジェクト指向プログラミング
-アスペクト指向プログラミング
-アダプティブプログラミング
-ArchJava
-関数型言語

二つ目の疎結合のセクションでは、以下が議論の対象になってる。
-ロールに基づくプログラミング
-Abstract, parameterized components
-implicit invocation and mediators


ここまで書いて力尽きたので続きはまた今度。

First International Conference on Design Science Research in Information Systems and Technology (DESRIST 2006)/Science of Design Symposium, 2007

 Science of Design wiki

のページ見てたら発見したんだけど、デザイン系のコンファレンス/シンポジウムの情報。

 First International Conference on Design Science Research in Information Systems and Technology (DESRIST 2006)

こっちはもう終了してるけど、プロシーディングがダウンロードできるみたい。デザイン理論とか気になる論文がちらほら。

 Science of Design Symposium, 2007 - A multidisciplinary approach to creativity and design

こっちは三月に開催らしい。

アーキテクチャ・メタプログラミング

Don Batory さんの次の論文を読んだ。

D. Batory,

Program Refactorings, Program Synthesis, and Model-Driven Design,

Invited Presentation, European Joint Conferences on Theory and Practice of Software (ETAPS) Compiler Construction Conference (April 2007).

DL: http://www.cs.utexas.edu/users/schwartz/pub.htm


色々適当に解説っぽいことをしながら、学んでみることにしよう。

「アーキテクチャメタプログラミング(Architectural metaprogramming)」とは何か。

プログラムリファクタリング、アスペクト指向などのプログラム合成、モデル駆動開発なんかは、彼によれば、アーキテクチャメタプログラミング技術とみなせるらしい。というのもこれらの技術は、プログラムを値とみなし、関数(変換)を使うことで、プログラムのプログラムにマップするため。

図で書くとこんな感じか(図は僕のオリジナル)。
arch_meta.png

arch_meta2.png



論文は、アーキテクチャメタプログラミングからではなく、ソフトウェアの複雑性の話から始まっている。

20年前に、Brooks さんは、プログラマーは、本質的な複雑性(essential complexity)ではなく、偶発的な複雑性(accidental complexity)において多くの時間を費やしている、と観測したらしい。

Batory さんによれば、多くの場合、これらの複雑性の違いをうまく言い表せないらしい。

それはさておき、Brooks さんの話によれば、複雑性には、上記の二つがあることになる。
complexity.png


この二つ複雑性について詳しくは、僕は知らないけど、Batory さんによれば、複雑性というのは、構造に関して制約を強いる(imposing structure)ことで制御できるらしい。で、この論文は、ソフトウェア構造の本質的な複雑性についてのものらしい。

で、プログラムリファクタリングとか、プログラム合成、モデル駆動開発なんかは、プログラム構成についてのもの。

ちなみに、論文では「ソフトウェア構造」と「プログラム構造」は厳密に区別していないように見える。


で、次に、アーキテクチャメタプログラミングの基本的なことについての話に移ってる。

上述のように、アーキテクチャメタプログラミングでは、プログラムを値と見なす。例として、次のような「値 C」と「値 D」が紹介されてた。

class c { // 値 C
int x;
void inc() {
x++;
}
}
class d { // 値 D
int compute() { }
}



値は加算することができる。加算 C + D は、クラス c と d からなるプログラム。

次の例。
arch_meta_eg.png



この後、足し算と引き算の特性についての議論があったけど省略。


次に、distributive transformations (DTs) と呼ばれるオペレーションについての話題。

DT の例は、rename(p, q, r) があるらしい。これは、プログラム p における名前 q を r で置き換えるらしい。

たとえば、名前 "x" を "x" で置き換えたいなら、こうする: rename(C2, xx, y)。この適用により、値 C3 が生じる。
rename.png



ここまで書いて力尽きたので続きはまた今度。

コード進化:向きを変える

コード進化を考えるパート3ぐらい。ちなみに、「コード進化のパターン」と「コード進化」は、関連するけど、区別してます。


今回は、ちょっとアプリケーション特化(ゲーム)な内容だけど、機能追加の要求がどのようにしてコードに影響を与えるのかを考察したい。

どんな機能追加か。次の図のような感じ。
look.png

追加したい機能は、右側のヤツ。既に左側の機能は実現済み。

機能追加を要求変化だと考えると、要求の変化は次のような感じ。
look_req.png


この要求変化に伴って、コード側もこの要求を満たすように変化しないといけない。ここでは、コード=デザインとしてる。
look_req_design.png


結論から言えば、コードの変化があったのは次の二つのクラス:
-KeyManager:どのキーがどんな機能に対応するのか管理 or キーコンフィグの実装。
-NormalModeKeyHandler:通常モードで押されたキーの処理を扱う。

追加されたクラスは:
-LookKeyHandler:通常モードでキャラの方向を変えるキーが押されたときの処理を扱う

先ほどの図で表すとこうなる:
look_req_design2.png


いつものように、デザイン構造の変化を DSM (Design Structure Matrix) で確認してみる。DSM は、要素間の依存関係を表現するための方法。
before:
look_dsm_before.png

after:
look_dsm_after.png


別の表現だとこんな感じ。

before:
look_dep_before.png

after:
look_dep_after.png


一応コードも一部載せてみる:
before:


public class KeyManager {

// アクションキー
private static int ATTACK_KEY = KeyEvent.VK_Z;
private static int MAP_KEY = KeyEvent.VK_A;
private static int STATUS_KEY = KeyEvent.VK_X;
private static int ITEM_KEY = KeyEvent.VK_C;
private static int WAIT_KEY = KeyEvent.VK_S;
private static int SYSTEM_KEY = KeyEvent.VK_SPACE;

// その他のキーフィールド

private static int MOVE_UP_KEY = KeyEvent.VK_UP;
private static int MOVE_DOWN_KEY = KeyEvent.VK_DOWN;
private static int MOVE_LEFT_KEY = KeyEvent.VK_LEFT;
private static int MOVE_RIGHT_KEY = KeyEvent.VK_RIGHT;

// その他のキーチェックメソッド

// 前後左右
public boolean isMoveUpKey(KeyEvent e) {
return isKey(e, MOVE_UP_KEY, MOVE_UP_KEY2, MOVE_UP_KEY3);
}
public boolean isMoveDownKey(KeyEvent e) {
return isKey(e, MOVE_DOWN_KEY, MOVE_DOWN_KEY2, MOVE_DOWN_KEY3);
}
public boolean isMoveLeftKey(KeyEvent e) {
return isKey(e, MOVE_LEFT_KEY, MOVE_LEFT_KEY2, MOVE_LEFT_KEY3);
}
public boolean isMoveRightKey(KeyEvent e) {
return isKey(e, MOVE_RIGHT_KEY, MOVE_RIGHT_KEY2, MOVE_RIGHT_KEY3);
}

// ... ナナメ
}
public class NormalModeKeyHandler implements GameModeKeyHandler {

private ItemMenu itemMenu;

public NormalModeKeyHandler(ItemMenu itemMenu) {
this.itemMenu = itemMenu;
}

public void handleKey(KeyManager keyManager, GameManager gameManager, KeyEvent e) {
if (keyManager.isStatusKey(e)) {
// ...

} else if (keyManager.isItemKey(e)) {
// ...

} else if (keyManager.isMapKey(e)) {
// ...

} else if // その他のキー
// ...

} else if (keyManager.isMoveKey(e)) {
new MoveKeyHandler(gameManager, keyManager).handle(e);
}
}
}
public class MoveKeyHandler {

private GameManager gameManager;
private KeyManager keyManager;

public MoveKeyHandler(GameManager gameManager, KeyManager keyManager) {
this.gameManager = gameManager;
this.keyManager = keyManager;
}

public void handle(KeyEvent e) {

if ( keyManager.isMoveDownKey(e) ) {
tryMove( Direction.DOWN );
}
else if ( keyManager.isMoveUpKey(e) ) {
tryMove( Direction.UP );
}
else if ( keyManager.isMoveLeftKey(e) ) {
tryMove( Direction.LEFT );
}
else if ( keyManager.isMoveRightKey(e) ) {
tryMove( Direction.RIGHT );
}
// ... ナナメ
}

private void tryMove(Direction dir) {
// ... 移動できるなら移動
}
}


after:


public class KeyManager {

// その他のキーフィールド

private static int MOVE_UP_KEY = KeyEvent.VK_UP;
private static int MOVE_DOWN_KEY = KeyEvent.VK_DOWN;
private static int MOVE_LEFT_KEY = KeyEvent.VK_LEFT;
private static int MOVE_RIGHT_KEY = KeyEvent.VK_RIGHT;


private static int LOOK_UP_KEY = KeyEvent.VK_KP_UP;
private static int LOOK_DOWN_KEY = KeyEvent.VK_KP_DOWN;
private static int LOOK_LEFT_KEY = KeyEvent.VK_KP_LEFT;
private static int LOOK_RIGHT_KEY = KeyEvent.VK_KP_RIGHT;



// その他のキーチェックメソッド

// 左右前後
public boolean isMoveUpKey(KeyEvent e) {
return isKey(e, MOVE_UP_KEY, MOVE_UP_KEY2, MOVE_UP_KEY3);
}
public boolean isMoveDownKey(KeyEvent e) {
return isKey(e, MOVE_DOWN_KEY, MOVE_DOWN_KEY2, MOVE_DOWN_KEY3);
}
public boolean isMoveLeftKey(KeyEvent e) {
return isKey(e, MOVE_LEFT_KEY, MOVE_LEFT_KEY2, MOVE_LEFT_KEY3);
}
public boolean isMoveRightKey(KeyEvent e) {
return isKey(e, MOVE_RIGHT_KEY, MOVE_RIGHT_KEY2, MOVE_RIGHT_KEY3);
}

public boolean isLookUpKey(KeyEvent e) {
return (isMoveUpKey(e) && isShiftDown(e)) || isKey(e, LOOK_UP_KEY);
}
public boolean isLookDownKey(KeyEvent e) {
return (isMoveDownKey(e) && isShiftDown(e)) || isKey(e, LOOK_DOWN_KEY);
}
public boolean isLookLeftKey(KeyEvent e) {
return (isMoveLeftKey(e) && isShiftDown(e)) || isKey(e, LOOK_LEFT_KEY);
}
public boolean isLookRightKey(KeyEvent e) {
return (isMoveRightKey(e) && isShiftDown(e)) || isKey(e, LOOK_RIGHT_KEY);
}

public boolean isMoveKey(KeyEvent e) {
// ...
}
public boolean isLookKey(KeyEvent e) {
// ...
}


// ... その他のメソッド
}
public class NormalModeKeyHandler implements GameModeKeyHandler {

private ItemMenu itemMenu;

public NormalModeKeyHandler(ItemMenu itemMenu) {
this.itemMenu = itemMenu;
}

public void handleKey(KeyManager keyManager, GameManager gameManager, KeyEvent e) {
// ...

if (keyManager.isStatusKey(e)) {
// ...

} else if // その他のキー
// ...
} else if (keyManager.isLookKey(e)) {
new LookKeyHandler(gameManager, keyManager).handle(e);


} else if (keyManager.isMoveKey(e)) {
new MoveKeyHandler(gameManager, keyManager).handle(e);
}
}
}
public class LookKeyHandler {

private GameManager gameManager;
private KeyManager keyManager;

public LookKeyHandler(GameManager gameManager, KeyManager keyManager) {
this.gameManager = gameManager;
this.keyManager = keyManager;
}

public void handle(KeyEvent e) {

// ...

if ( keyManager.isLookDownKey(e) ) {
look( Direction.DOWN );
}
else if ( keyManager.isLookUpKey(e) ) {
look( Direction.UP );
}
else if ( keyManager.isLookLeftKey(e) ) {
look( Direction.LEFT );
}
else if ( keyManager.isLookRightKey(e) ) {
look( Direction.RIGHT );
}
}

private void look(Direction dir) {
// ... 方向変更
}
}




さらに違う表現をしてみよう。この表現の目的は、仕様間のオーバーラップを表すこと。キャラ攻撃の(既にある)仕様を加えていることに注意。これは、KeyMangaer と NormalModeKeyHandler が依存している仕様を少し複雑に見せたいため。
before:
look_concern_before.png

after:
look_concern_after.png


さて、この最後の図を基に、さらに仕様(or コンサーン)のオーバーラップを複雑にしていきたいと思う。目的は、コンサーンの分離の可能性を考えること。現在の図からは KeyMangaer と NormalModeKeyHandler は複数の仕様(キャラ移動、キャラ攻撃、キャラ向き変更に依存していることが分かる。

この依存をどう考えるべきか。たとえば、 ある仕様を機能と考えると、要求に合わせて取ったり付けたりしやすいか。ソフトウェア工学の言葉でいえば、プロダクトラインを構築できるのか。各機能をフィーチャモジュール(「Understanding Feature Modularity」を参照(すると良いかも。僕もまだちゃんと読んでないけど)としてモジュール化できるか。

というところを考えたいんだけど、力尽きたので、図だけ。
look_concern_more.png



やりたかったけどできなかったこと:
-フィーチャ合成・分割(feature composition/decompostion)について。たとえば、防御キーをフィーチャとするなど。あと、操作説明のビューへの影響について。

-仕様 or コンサーン依存について。

-アーキテクチャのルールが課すことによる進化の方向付けについて。
前のページ 次のページ

FC2Ad

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