asatoの技術的な日常日記

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

スポンサーサイト

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

ソフトウェア進化:進化ユニット

更新履歴
2008.02.20:図を追加。
2008.02.20:分かりにくい部分を少し修正。

はじめに


要求仕様の変更や保守性の向上など品質特性の変更に対応するために、ソフトウェアの変更が必要な場合があります。通常、以下の理由のため、ソフトウェアの変更は集団的、つまり連続的な変更です。

 (1)仕様変更や品質特性の変化の粒度の単位が、プログラムの粒度の単位と一対一で対応することは少ない。

req_and_program_gra2.png


 (2)ソフトウェアを構成する要素は、独立ではなく互いに依存しあっているため、ある要素の変更は他の要素の変更を要求する。
program_depend2.png


ここでは、要素間の依存関係に伴う変化を考えます。

明示的な依存関係に伴う変化:1つは、プログラム要素間に形式的な依存関係がある場合です。その場合、依存されている側の要素を変更すると、その要素に依存している要素にも変更が及びます。たとえば、変数名を変更すると、その変数を参照しているコード要素も変更が必要です。変更が要求されるかどうかは、コンパイラやインタプリタ(もしくは依存関係を形式的に定義し、検証できるシステム)がエラーを出すかどうかで分かります。

特徴は、依存関係の定義は、形式的かつ客観的であるため、どんな変更が必要なのかがわかることです。

暗黙的な依存関係に伴う変化:プログラム要素間に明示的な依存関係がないのにもかからわず、あるプログラム要素を変更するとき、その変更に伴って他の要素を変更する場合があります。この種の変化を、暗黙的な依存関係に伴う変化と呼ぶことにします。

この記事では、暗黙的な依存関係に伴う変化に対して考察してみます。特に、暗黙的な依存関係に伴って変化するコード群を、ここでは 進化ユニットと呼びたいと思います。この進化ユニットの観点から考察します。

進化ユニットの定義は今のところ曖昧です。ユニットという言葉が適切かどうかもわかりません。しかし、設計者もしくはプログラマーがなぜこの種の変更を行うのかを原理などの観点から適切に説明するのは困難であると感じています。

実例



実例を紹介します。次のようなコードがあるとします。ゲーム画面を描画するコードです。

コード状態A:


public class StatusViewDrawer implements GameViewDrawer {

// フィールド
public void draw(Graphics g) {
// .. コード

// ここから、
drawStringWithShadow(g, "武器  ", 250, baseY + 30);
if (player.getStrItem() instanceof NullStrItem == false) {
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);
}

drawStringWithShadow(g, "防具  ", 250, baseY + 60);
if (player.getDefItem() instanceof NullDefItem == false) {
drawStringWithShadow(g, defItem.getName(), 360, baseY + 60);
}

drawStringWithShadow(g, "人工精霊", 250, baseY + 90);
if (player.getSpiritItem() instanceof NullSpiritItem == false) {
drawStringWithShadow(g, spiritItem.getName(), 360, baseY + 90);
}
// ここまでが進化ユニット

// .. コード&その他のメソッド
}
}

public class NullStrItem extends StrItem { // Null Object

// ... その他のメソッド

@Override
public int getStrPoint() {
return 0;
}
}

public class NullDefItem extends DefItem {
// ... 省略
}

public class NullSpiritItem extends SpiritItem {
// ... 省略
}



次の図は、上記のコードによって描画されたゲーム画面を表します。
suisei_status_view2.png


NullStrItem といったクラス名から想像できるように、このコードでは Null Object パターンが適用されています。 ここで、Null Object パターンが適用されているのに、あるオブジェクトが Null Object かどうかを判別するコードがあるのは良くない兆候だと考えたとします(実際にこれが適切な判断かどうかは後の議論に関係ありません)。



if (player.getStrItem() instanceof NullStrItem == false) { // Null Object かどうか
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);
}



そのため、次のようにコードを変更することにしました。

コード状態B:


public class StatusViewDrawer implements GameViewDrawer {

// フィールド
public void draw(Graphics g) {
// .. コード
// ここから、

drawStringWithShadow(g, "武器  ", 250, baseY + 30);
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);

drawStringWithShadow(g, "防具  ", 250, baseY + 60);
drawStringWithShadow(g, defItem.getName(), 360, baseY + 60);

drawStringWithShadow(g, "人工精霊", 250, baseY + 90);
drawStringWithShadow(g, spiritItem.getName(), 360, baseY + 90);

// ここまでが変更された

// .. コード&その他のメソッド
}

}

public class NullStrItem extends StrItem {
// ... 他のメソッド
@Override
public int getStrPoint() {
return 0;
}

@Override
public String getName() {
return "";
}

}

public class NullDefItem extends DefItem { // 同じように getName を追加
// ... 省略
}

public class NullSpiritItem extends SpiritItem { // 同じように getName を追加
// ... 省略
}



code_state_a_b.png


コード状態AからBへの変更の過程は上の図に示しめしている通りです。メソッドレベルの変更粒度で見た場合、変更過程は次のようになります。ただし、順番は考慮しません。

 (変更a) NullStrItemクラスへgetNameメソッドを追加

 (変更b) NullDefItemクラスへgetNameメソッドを追加

 (変更c) NullSpiritItemクラスへgetNameメソッドを追加

 (変更d) StatusViewDrawerクラスのdrawメソッドを変更


疑問



ここでの疑問は、なぜこのような変更が起こったのか、です。このような変更過程を引き起こす要因は何か、ということです。たとえば、NullStrItem クラスに getName を追加したからといってなぜその他の NullDefItem と NullSpiritItem も変更したのでしょうか?

この集団的な変更は、明示的な依存関係によって発生したものではないため、変更する人によっては異なる変更結果となりえます。たとえば、NullStrItem クラスにだけ getName を追加し、描画のコードも次のようにだけ変更される可能性もあります。

コード状態C:


public class StatusViewDrawer implements GameViewDrawer {

// フィールド
public void draw(Graphics g) {
// .. コード
// ここから、
drawStringWithShadow(g, "武器  ", 250, baseY + 30);
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);
// ここまでが変更された


drawStringWithShadow(g, "防具  ", 250, baseY + 60);
if (player.getDefItem() instanceof NullDefItem == false) {
drawStringWithShadow(g, defItem.getName(), 360, baseY + 60);
}

drawStringWithShadow(g, "人工精霊", 250, baseY + 90);
if (player.getSpiritItem() instanceof NullSpiritItem == false) {
drawStringWithShadow(g, spiritItem.getName(), 360, baseY + 90);
}
// .. コード&その他のメソッド
}
}

public class NullStrItem extends StrItem {
// ... 他のメソッド
@Override
public int getStrPoint() {
return 0;
}

@Override
public String getName() {
return "";
}

}



code_state_a_b_c.png



考察



NullStrItem に対して、getName をオーバーライドして定義しましたが、そのメソッドの導入が次のコードの変更を示唆することは分かりません。

変更前:

    drawStringWithShadow(g, "武器  ", 250, baseY + 30);    

if (player.getStrItem() instanceof NullStrItem == false) {
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);
}



変更後:

   drawStringWithShadow(g, "武器  ", 250, baseY + 30);
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);


また、NullStrItem に対する、getName メソッドの追加が、NullDefItem への getName の追加を示唆することは分かりません。

示唆がないのに、どうしてこのような変更を行うのでしょうか?


たとえば、次のようなシナリオを考えます。上記のようにコードを変更しようと思ったプログラマAと、変更の一部をまかされたプログラマBがいると考えてください。

次に、プログラマAが、次のコード状態Xから、コード状態Yに変更したとします。目標となるコード状態はコード状態Bだとします。
コード状態X:


public class StatusViewDrawer implements GameViewDrawer {

// フィールド
public void draw(Graphics g) {
// .. コード

drawStringWithShadow(g, "武器  ", 250, baseY + 30);

if (player.getStrItem() instanceof NullStrItem == false) {
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);
}

drawStringWithShadow(g, "防具  ", 250, baseY + 60);
if (player.getDefItem() instanceof NullDefItem == false) {
drawStringWithShadow(g, defItem.getName(), 360, baseY + 60);
}

drawStringWithShadow(g, "人工精霊", 250, baseY + 90);
if (player.getSpiritItem() instanceof NullSpiritItem == false) {
drawStringWithShadow(g, spiritItem.getName(), 360, baseY + 90);
}

// .. コード&その他のメソッド
}
}

public class NullStrItem extends StrItem { // Null Object

// ... その他のメソッド

@Override
public int getStrPoint() {
return 0;
}
}




コード状態Y:


public class StatusViewDrawer implements GameViewDrawer {

// フィールド
public void draw(Graphics g) {

// ..その他の コード

drawStringWithShadow(g, "武器  ", 250, baseY + 30);

if (player.getStrItem() instanceof NullStrItem == false) {
drawStringWithShadow(g, strItem.getName(), 360, baseY + 30);
}

drawStringWithShadow(g, "防具  ", 250, baseY + 60);
if (player.getDefItem() instanceof NullDefItem == false) {
drawStringWithShadow(g, defItem.getName(), 360, baseY + 60);
}

drawStringWithShadow(g, "人工精霊", 250, baseY + 90);
if (player.getSpiritItem() instanceof NullSpiritItem == false) {
drawStringWithShadow(g, spiritItem.getName(), 360, baseY + 90);
}
// .. コード&その他のメソッド
}

}

public class NullStrItem extends StrItem {
// ... 他のメソッド
@Override
public int getStrPoint() {
return 0;
}

@Override
public String getName() { // この部分を追加
return "";
}

}



次に、プログラマBはプログラマAと、直接的・間接的に、会話もしくは文書によりコミュニケーションをとっていないとします。

次に、プログラマBにコード状態Yが渡されたとします。プログラマBは、コード状態Bに到達するでしょうか?


分かるのは、プログラマBはプログラマAからの何らかの情報が必要だということです。ここでの疑問は、その情報が形式的にどのような情報なのか、ということです。つまり、単に「このコードをこんな感じにこうやって変更しておいて」というような曖昧なやりとりではないということです。

今のところの仮説としては、以下のものです。

 (仮説1)コードの構造、もしくはより抽象的な設計の構造が存在するとき、その構造には、変更対する変更ルールが存在する。

 (仮説2)プログラム要素は、このような変更ルールとの依存関係がある。

 (仮説3)プログラム要素と変更ルールとの関係が、進化ユニットを形成する。


設計の構造とは、ここでは、デザインパターンレベルの抽象度の構造をさします。上記の例では、Null Object パターンを適用しています。

変更ルール(抽象度の低い場合には、コーディングルール、高い場合には設計ルール)が存在するのは当然だと思われるかもしれません。設計書が必要な理由もそこにあるとも言えます。しかし、次のような基本的なことはそれほど明らかではありません。

 (A)ある(部分的な)設計構造が与えられたとき、どのような変更ルールが存在しうるのか。

 (B)そしてその変更ルールはどのような原理・原則に基づいているのか。

たとえば、あるデザインパターンが適用された設計構造があるとして、その設計構造の変化にはどのような種類があるのでしょうか? 具体的は、たとえば、Factory パターンが適用された構造に対してどのような変化が起こりうるのでしょうか? 1つは、concrete factory の役割を持つクラスの追加です。

変更前:
add_concrete_factory_before.jpg


変更後:
add_concrete_factory_after.jpg


Factory パターンのような良く知られたパターンに対してさえ、どのような変更が存在し、その変更がどのような変更ルールに従っており、そしてまたそれらの変更ルールがどのような原理・原則から発生するのかはわかっていません。


まとめと今後の課題



この記事では、進化のユニット という概念を提案しました。進化ユニットが形成される仮説を議論しました。今後は仮説の検証を行います。

設計原則とは何か or 設計原則のメタモデル, パート1

はじめに


ソフトウェア工学の理論と実践では、いくつかの設計原則が知られていると思う。たとえば Parnas の情報隠蔽など。

そもそも、ソフトウェア開発における設計原則とは何だろう? 個々の原則は、どんな要素から成り立っているんだろうか? 原則間にはどんな関係があるんだろう? というようなメタ(オントロジー)な視点から、設計原則を考えていきたい。

原則の定義


まずは、「原則」そのもの定義について考えてみる。

goo の国語辞典で「原則」を調べてみるとこうある。

(1)多くの場合にあてはまる基本的な規則や法則。しばしば原理と区別せずに用いられるが、原理は主として存在や認識に、原則は主として人間の活動に関係する。
「―として五時に下校すること」

(2)〔論〕〔(ドイツ) Grundsatz〕他の諸命題がそこから導き出される基本命題。



マイケル・ジャクソンさんは、「ソフトウェア要求と仕様」という本で、次のように書いている。

原則とは、ソフトウェア開発作業と方法論を律すべきだと私が信じる事柄である。



ソフトウェア工学・システム工学ハンドブック」という本では、「原則」とは書かれていないけれど、「原理」として次のように書かれている(principleは原則あるいは原則と訳される)。

すべての経験が法則と呼べるようになるものではありません。多くの場合、普遍性が足りなかったり、検証が不十分だったりしています。かなり詳細なレベルのものも多くあります。それでも有用なものについて、本書では「原理(principle)」として参照しています。



設計原則の例


設計原則の例として、まずは Parnas の情報隠蔽を考えてみる。

16.3.2 Design Principle

Our module structure is based on the decomposition criterion known as information hiding [6]. According to this principle, system details that are likely change independently should be the secrets of separate modules; the only assumptions that should appear in the interface between modules are those that considered unlikely to change. Each data structure is used in only one module; it may be directly accessed by one or more programs within the module but not by prigrams outside the module. Any other program that requires information stored in a module's data structures must obtain it by calling access programs belonging to that module.

-----
D. L. Parnas, P. C. Clements, D. M. Weiss, The modular structure of complex systems, Software Fundamentals: Collected Papers by David L. Parnas, p. 321. [PDF オリジナルバージョン?]



(1)情報隠蔽は、モジュール分割のための基準である。
(2)情報隠蔽は、原則である。

二つとも、論文の定義をそのまま利用。ただし、2番目については、principle という言葉をわざわざ定義していないので注意が必要。というのも「ソフトウェア工学・システム工学ハンドブック」では、情報隠蔽を原則ではなく法則として紹介しているから。この本によると、法則とは次のようなものらしい。

... 本書ではきわめて重要な教訓を「法則」と呼んでいます。

... 法則(law)という用語に関連して別の意味がありますが、本書においては「自然の法則」という意味での「法則」です。ウェブスター辞書の定義によれば、自然の法則とは「ある条件の下で一定の現象の順序や関係を提示するもの」です。

... 本書では「法則(Law)」、「仮説(Hypothesis)」、「推測(Conjecture)」を区別しています。ある規則を「法則」として引用することで、それを支援する強力な実証的(エンピリカルな)証拠があること、そして重要な洞察を表していることを意味しています。



原則と法則の区別はともかく、情報隠蔽を例に、設計原則として一般化してみよう。他の設計原則も対象にして一般化するべきだと思うけど、まずは、情報隠蔽だけで考えてみる。

(a)ある設計原則には、名前がある

(b)ある設計原則は、何かについての基準である

aは、受け入れてもいいと思う。bについては、議論が必要。

(A)「何かについての基準」ではない設計原則が存在するかもしれない。

(B)何かについての基準だとしても、基準は一つだけとは限らないと思う。

これらについては、今後具体的な他の(設計)原則を例として調査していきたい。たとえば、「ソフトウェアアーキテクチャ」という本で紹介されている原理が参考になるかもしれない。


 いくつかの基礎となる原理に基づいて、ソフトウェア構築は行われる。その基礎となる原理を「根底技術」(enabling techniques)と呼ぶことにする。その原理は、時間の経過によって曖昧なものになってしまったために、特定の名前が付けられていない。この広く受け入れられてしまっている原理を実現した技術が開発されており、原理と技術の区別が難しくなってきている。ここでは、単純にこれらを同義だと見なすことにする。

...ソフトウェアアーキテクチャのためのパターンはこれらの原理に基づいており、パターンの大部分はこの原理のどれか一つに焦点をあてている。本節では、ソフトウェアアーキテクチャのための「根底技術」のうち最も重要なものをまとめておく。

・抽象

・カプセル化

・情報隠蔽

・モジュール化

・関心の分離

・結合度と凝集度

・充足性、完全性、プリミティブ性

・ポリシーと実装の分離

・インタフェースと実装の分離



その他参考


ソフトウェアに限らず、原則自体について僕が調べたのがあるので、参考に。
-原則の原則
-原則の特徴

デザイン:設計順序の原則

更新履歴:
2008.02.27 - 読みやすいように&議論を少し修正。

 疑問:実現すべき機能要求の集合が与えられたとして、どの順番でそれらの機能を設計・実装していくのが適切なのか?

疑問


少し前に気づいて疑問に思ったことだけど、実現すべき機能要求の集合が与えられたとして、

 (1)どんな順番で機能を設計・実装していくのがいいんだろうか?

 (2)順番は最終的な設計・実装結果に影響を与えないのだろうか?

ここでは、簡単のために機能的な要求だけを考えてる。非機能要求の考慮は保留。

議論


さて、自分自身に問い合わせてみたところ、少なくとも一つの原則に従って(従おうとして)設計・実装していることに気づいた。それは、抽象がなるべくできあがるような順番で機能を設計・実装しようとしている、ということ。

もっと具体的に言えば、ある機能AとBを設計・実装することで、抽象的なモジュール(たとえば抽象クラスやインタフェース)が出来るような順番。

たとえば、ゲームを作っているとし、次のような機能要求集合が与えられたとする。

 (1)キーボードによる操作ができること
 (2)マウスによる操作ができること
 (3)ゲームコンフィグができること。
 (4)ゲームデータがセーブできること
 (5)ゲームデータがロードできること

とすると、順番としては、たとえば次のようなものを選ぼうとする傾向があった。

 (A) (1)->(2)
 (B) (2)->(1)

これは、1と2がどちらも操作に関わる機能であるため、モジュール(クラス)として実現したときに共通の特性を持つ抽象(抽象クラスや interface)ができる可能性がある、という理由から。つまり、

 (a) コードの重複や共通のインタフェースができるだろうということ。

 (b) そして、操作に関わる機能を実現したモジュールを使うモジュールは、抽象を通して依存すべき、という原則の点から。

AとBのどっちを優先するかは機能の優先順位によるけれど、どちらもある一定の期間中に実現できればいいとするなら、AとBの順番は気にしない。

もちろん、個々の要求が独立しているとは限らないので[2]、実現の順序がほとんど決まってしまう場合もある。たとえば、機能Aを実現する前に機能Bができていなければならないなら、B->Aの順番で実現するのが普通だと思う。ただし、AとBの間にインタフェースI(もしくはデザインルール[3])を導入するなら、AとBは独立に実現できる可能性もある。


今まで考えたことがなかったので、読み逃しているかもしれないけど、僕が知る限り、機能の設計・実装の順序について議論している本はないように思える。

設計の原則はある。たとえば「アジャイルソフトウェア開発の奥義」では、次のようなオブジェクト指向における設計原則を解説している(わざわざ英語で書いてるのは、邦訳を持ってないため。


・The Single Responsibility Principle - A class should have only one reason to change.

・The Open-Closed Principle - Software entities (classes, modulues, functions, etc.) should be open for extension, but closed for modification.

・The Liskov Substitution Principle - Subtype must be substitutable for their base types.

・The Dependency Inversion Principle - Abstraction should be not depend upon details. Details should depend upon abstractions.

・The Interface Segregation Principle - Client should not be forced to depend upon methods that they do not use. Interfaces belong to clients, not to hierarchies.

・The Release-Reuse Equivalenct Principle - The granule of reuse is the granule of release.

・The Common Closure Principle - The classes in a package should be closed together against the same kinds of changes. A change that affects a closed package affects all the classes in that package and no other packages.

・The Common Reuse Principle - The classes in a package are reused together. If you reuse one of the classes in a package, you reuse them all.

・The Acyclic Dependencies Principle -
Allow no cycles in the package dependency graph.

・The Stable Dpendencies Principle - Depend in the directions of stability.

・The Stable Abstractions Principle - A package should be abstract as it is stable.



上記以外の設計原則としては「Refactoring in Large Software Projects 」で紹介されているのがある(上記と被る原則は省略)。

・Don't Repeat Yourself - Do not write the same or similar code more than once.

・Speaking Code Principle -
The code should communicate its purpose. Comments in the code could indicate that the code communicate its purpose insufficiently.

・Tell, Don't Ask -
Don't ask an object about an object, but tell it what to do.




でもこれらの設計原則は、プロダクトとしての設計、つまり具体的にはモジュール、プログラム、コードの構造が備えているべき特性や品質の原則に思える。

もしかすると、プロセスの結果としてのプロダクトに関する原則は、プロセスに関する原則との表と裏の関係かもしれない。

それでも、プロセスとしての設計原則を明示的に表しておくのは、効果的であると思う。

たとえば、一つの具体的なものから抽象的なものを考えるよりも、二つの具体的なものから抽象的なものを考える方が易しい。これを設計プロセスの(仮の)原則として言うなら、こんな感じ:

 抽象が作りやすくなるような順番で、機能を設計・実装せよ。


参考


[1] ロバート・C・マーチン, アジャイルソフトウェア開発の奥義

[2] アラン・M・デービス, 成功する要求仕様 失敗する要求仕様

[3] キム・クラーク,カーリス・ボールドウィン, デザイン・ルール―モジュール化パワー

番外編


今回の記事の内容に直接関わるかどうかは分からないけど、Christopher Alexander の「The Nature of Order」のBook 2では、順番についての次の記述がある。


4. VITAL IMPORTANCE OF THE "RIGHT" SEQUENCE

In architecture, as in other things, the importance of sequence is both simple, and potentially shocking. Even when there are only two steps to be token, the order in which they are done may be all-important. And it may be highly surprising.

Consider for example the process of laying out a house on a lot.

"Common sense" says that you should first place the house, and then place the garden, This is common sense; but it is wrong. To make the environment and the formation of house garden together come out right, you have to reverse the order of these two operations, as follows:
1. First: Locate the garden in the best and most beutiful place.
2. Then: Locate the house so that i helps and supports the garden.

This example is fascinating because it illustrate the enormous significance of sequence. If you place the house first, you are stuck with the leftovers as places for the garden. In all likelihood the garden will not become a pleasant place. Possitive space will amost certainly be violated. So will view, smell, noise. But if you locate the garden first and then place the house volume in a way which supports the wholeness of the garden, the garden will come out better and so will the house.

Of the two available sequence for these two operations, one sequence is wrong, and ohe is right (90% of the time). The sequence which is wrong, is correct according to conventional wisdom, and probably considered obviouse by millions of people. The one which is right (and which gives you insight, makes things more understandable, and gets wholesome results), flies in the face of common sense, and would be rejected by nine out of ten people who first hear it. Yet it is the right answer.

Few examples explain the enormouse power and siginificance of sequences more vividly. And as you can imagine, if the sequence -- the order of steps -- is significant when dealing with only two operations, how much more siginificant it will be when dealing with ten, or twenty, or fifty steps. There the chance of getting the sequence right by accident, is extremely small.



本:Software Evolution

ソフトウェア進化本。amazon で検索したら発見した。


Software Evolution,

by Tom Mens (Editor), Serge Demeyer (Editor)

Springer; 1 edition (February 2008)

http://www.amazon.com/Software-Evolution-Tom-Mens/dp/3540764399/




ソフトウェア進化本としては「Software Evolution and Feedback: Theory and Practice」に続き二冊目?

メモ:科学と定量化

更新履歴
2008.03.05:「Designerly Ways of Knowing: design discipline versus design science」への参照を追加。
2008.01.05:論文のタイトルを載せました。


デザイン科学という言葉が気になる。「なんとか科学」、そもそも科学とは?

ドラッカーによれば:

 科学とは、多くの専門家が考えているような、定量化のことではない。もしそうならば、占星術は科学の女王となる。占星術は科学そのものでないことはもちろん、科学の応用でさえない。占星術は、現象を観察し、一般化して仮定とし、その仮定を観察によって検証する。しかしそれでも占星術は、科学ではなく迷信である。古代の航海上の記憶のヒントにすぎなかった獅子や魚への連想から、星座とその黄道内の動きに意味を与えるなどということなど、子どもじみた迷信以外の何者でもない。

 つまり、科学であると言いうるには、一貫した整合的かつ総合的な前提、そして公理の構築に加えて、科学の対象となる世界、すなわち有意なる現象を合理的に定義しなければならない。しかも、この科学の世界に関する定義と基本とすべき公理の構築は、いかに雑なものにとどまろうとも、科学的な手法を適用する前になさなければならない。これがあって、初めて科学的な手法が適用可能になり、大きな力を発揮しうるのだ。


P.F. ドラッカー経営論, 「経営科学の罠」, p. 79. より。

もちろん、ウィキペディア先生も参考に。

デザインと科学との関係について参考になる論文。


N. Cross, Designerly Ways of Knowing: design discipline versus design science, Design Issues, Vol. 17, No. 3, pp. 49-55, 2001.


デザイン:Design Research Quarterly

またまた、Nigel Cross さんのページ みてたら発見。オンラインの雑誌かな? よくわからん。

 Design Research Quarterly

本:Design Research Now

設計研究本。Nigel Cross さんのページ見てたら発見した。



Design Research Now: Essays and Selected Projects

Design is becoming a recognised academic discipline, and design research is the driving force behind this transformation. Design Research Now Essays and Selected Projects charts the field of design research with introductory essays and selected research projects. The authors of the essays, all leading international design scholars, stake out positions on the most important issues of design research. They locate the significance of design research at the interface with technological development, describe what makes it a necessary ingredient of the continued development of the design disciplines, and assign it a seminal role in the relevant developments of society.

The essays are supplemented by the presentation of recently completed research projects from universities in the Netherlands, the UK and Italy.


コード進化パターン:フィールドの削除

昔から書いてる、

 Java におけるコード進化パターン

を更新。今回は「フィールドの削除」を追加。

話としては、(一見)すごく単純。単にフィールドを削除する場合がありますよ、というだけ。

コードで書くとこれだけ。
before:

public class MyClass {

private String s;
}



after:

public class MyClass {
}



難しいのは、どんな状況の時に、フィールドを削除することになるのか、 という点。

いくつかの状況は考えられる。でも、それらの状況を分析しようとしている人はそんなに多くないと思う。

たとえば、フィールドを削除するだけ、というコードの進化は普通は起こらない。フィールドはどこかで使われているはずだから、メソッドボディの変更も伴う。

デザイン:表現の定義

前回の記事(『デザイン:対象と操作』)に関係することを補足。単なる引用だけど。

Simon によれば、表現を定義するとは次のことらしい。

Defining a representation means (1) specifying one or more data types, and (2) specifying the primitive (i.e., "fast") operations that can be performed on information in those data types.


一部しか引用してないけど、詳細を知りたい方は次の論文を参照。

 On the Forms of Mental Representation

ちなみに、元々この論文の存在を知ったのは、 Vinod Goel さんの「Sketches of Thought」という本の139ページで紹介されてたから。


さて、表現の話がなぜデザイン(設計)と関わるのか? そもそも、何を 表現したいのか? これに関しては、(プロダクトとしての)デザインといえる。正確に言えば、もしデザインという行為が、意思決定のプロセス だとするなら、その出力を表現したい。

という流れ。

デザイン:対象と操作

ソフトウェアは、もちろん成果物。成果物を出力するには、なんらかのプロセスが必要。

基本的には、ソースコードをコンパイルして得られるのがソフトウェアだと思う(スクリプトの話はなしで)。

プログラマーは、コードを編集する。要求仕様を満たすように編集する。

さて、ソースコードを抽象化して考えてみよう。ここでの抽象化とは、コメントや空白などを除くことを意味する。抽象化した対象にたいして、プログラマは編集操作を行う。Java プログラムの一部を具体例として考えてみよう。

抽象化した対象をどう表現するか。ここでは、Eclipse なんかでであるようにGUIで表現できるとする。もしくはもっと簡単に、変数名と値を表示するだけとする。

operator_class.png


これで対象が決まったので、次は対象に対する操作を考えてみる。一番単純なのは、クラス名を変更するといった操作。

operator_class2.png


ここで言いたいのは、対象が決まれば、可能な操作が決まるということ。もちろん、可能な操作は勝手には決まらない場合もある。

ソースコードに対する編集操作と、ソースコードを抽象化した表現対象に対する操作は異なる。ソースコードに対する操作には、たとえば、「コメントを追加する」や「空白を追加する」「タブを追加する」「改行する」などの操作が考えられる。しかし、そのような操作は、抽象化した対象に対しては意味を持たなくなる。


次に、操作を抽象化することについて考えてみよう。代表的なのはリファクタリング。たとえば、「スーパークラスの抽出」リファクタリングを考えてみよう。

operator_refactor.png


ここで言いたいのは、抽象化された対象に対する操作のみを使って、より抽象度の高い操作を表しているということ。つまり、「クラスを作成する」などの操作を行ってはいるけどソースコードレベルの操作、たとえば「改行する」などは操作として含まれていないという点。


さて、ここからが今回の話のコアとなる部分。過去の記事で何度か設計という行為は、意思決定の観点から考えられると言ってきた。考えられるとは、つまり、意思決定の観点から抽象化して考えられるとしてきた。

意思決定とは、選択肢からの選択を意味する。つまり、ある操作を表す。そして、操作があるということは操作対象が存在する。

具体例で考えてみよう。状態遷移を実現する必要がある場合、どんな決定を行うだろうか? 選択肢としては、「ifによる分岐」「Stateパターン」「遷移テーブル」などがあると思う [アジャイルソフトウェア開発の奥義]。

ここでは、Stateパターンを選択したとする。

operator_state.png


Stateパターンの基本構造は、こんな感じ。
operator_state2.png



次にStateパターンが適用された構造に対する操作を考えてみよう。二つの観点から考えてみる。

(1)Stateパターンかどうかに関係なくできる操作。たとえば、Stateパターンが適用されていると知らなかったとする。すると、どんな操作が可能か。

(2)Stateパターンが適用されているからできる操作。


まず一つ目だけど、どんな操作でもできる。Stateパターンでなくなるような操作ですら可能。



次に二つ目だけど、たとえば、「ConcreteState」を追加・削除するなんてのは適切な操作の一つ。他にも、request/handleメソッドのペアを追加・削除するなんてのも可能な操作。一方で、request メソッドのみを削除するなんてのは不適切な操作。


まとめ:

対象が決まれば、可能な操作が決まる。

前のページ 次のページ

FC2Ad

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