Open iteman opened 9 years ago
負の可変性の話と、 https://twitter.com/esumii/status/561996511358107648 の「円-楕円問題」
「円を楕円のサブクラスにすると、『楕円を横に伸ばす』メソッドがあったら、円も実装する必要があって困る(意訳)」 これだからオ…いや何でもないです
- 人間の自然な感覚で、「円」と「楕円」のどちらが「一般的」なのか。
- 数学的な定義(円は、楕円の一種である)と反対になっているのではないか
- 負の可変性を認めれば、素直に解決できるのではないか
3.1 可変性:生の芳香 (p.71)
共通性が設計の骨格であり、骨組みを形成するものなのに対して、可変性は血肉であると言える。アーキテクチャから考えれば、共通性分析はアーキテクチャを永らえさせるものであり、それに対して可変性分析はそのアーキテクチャを利用し得る形に仕立て上げるものということになるだろう。
具体的な例を挙げると、ドメイン特化言語が可変性を表現し、その背後にあるドメインコンポーネントが共通性を表現するということになる。
3.1 可変性:生の芳香 (p.72)
アプリケーションドメインの構造は、同一の次元軸に沿って可変させることができる。その軸を決めるのが共通性である。
選択された共通性の設計次元によって可変性の特性が定まるということだと思う。
3.1 可変性:生の芳香 (p.72)
可変性分析と共通性分析の両者で共通性カテゴリの用語を使用するので、この2つのアクティビティは同時に実施されるべきだろう。
3.1 可変性:生の芳香 (p.73)
ドメインエンジニアリングは共通性分析と可変性分析を支援する。そしてその際に、共通性、可変性、バインディング時期、デフォルト、ドメイン関係を把握するための記法を利用する。共通性分析と可変性分析の成果物は、その記法で表現された成果の集合に成る。可変性分析を終えた段階で、多くの場合、これらの成果物を下位レベルの設計(クラスと関数プロトタイプを書くこと)への直接の入力とすることができる。
具体例は第9章 共依存ドメイン 9.3 例:状態遷移マシン pp.252-259
を参照のこと。
3.3 正の可変性と負の可変性 3.3.1 正の可変性 (p.74)
メッセージサイズとヘッダフィールドの内容に関する可変性は、メッセージをメッセージたらしめる共通性からは独立している。メッセージは、ヘッダに空のフィールドがあろうとも、依然としてメッセージである。1バイトの本体を持っていようと、256バイトの本体を有していようと、1個のメッセージとして、自由に活動することができる。しかし、ヘッダフィールドのフォーマットとメッセージ本体のサイズは、重要な可変性になる。このような可変性は、その基となる共通性のモデルに影響を与えないので、メッセージにそれ自体の定義を洗練させるような何かを「追加する」ことになる。著者は、これを正の可変性(positive variability)と呼んでいる。この可変性のために、メッセージをメッセージたらしめるベース仕様に対して項目が追加されることになるが、そのオリジナルの仕様は何ら損なわれることがないからである。
3.3 正の可変性と負の可変性 3.3.2 負の可変性 (p.75)
メッセージは必ず本体を持つのだとすると、メッセージ本体のサイズを限定するのは可変性である(ここでは、本体のフォーマットはメセージの設計に無関係であると仮定している)。メッセージの大半が本体を持つが、持たないものも存在するのであれば、可変性が共通性の仮定を破壊することになる。つまり、我々が「メッセージ」の意味であると定義するものの根底にあるとした想定を、可変性が否定するのである。これを負の可変性(negative variability)と呼ぶ。負の可変性は正の可変性とはまったく異なるものである。
3.4 ドメインと可変性レンジ (p.77)
1個のファミリとは、特定の設計ニーズに適い、1個の「仮想的なマシン」によって生産可能な構成員の集まりだと考えよう。
そのマシンが生産可能であるファミリ構成員の全体を指して、レンジ(range、値域)という用語を用いる。
ここで述べているマシンは、入力、ノブ、スイッチ、レバーを持ち、それらにより構築しようとするファミリ構成員の特性を制御することができる。この入力を可変パラメータ(parameter of variation)と呼ぶ。
ドメイン(domain、定義域)という用語は、1個のファミリのすべての可変パラメータに対する正当な値の組み合わせ、あるいは、その可変パラメータのうちの限定されたいくつかのものに対する正しい値の組み合わせを示している。
3.4 ドメインと可変性レンジ (p.77)
共通性分析によって、重要な共通性を把握しようと考えるのが常である。それにより、再利用が促進されるからである。しかし、可変性を疎かにしてよいとは考えていない。疎かにするのではなく、それに規則性を与えたいのだ。数多くの可変性を同値集合(equivalence)に抽象化することができるのであれば、少数の単純なパラメータですべての可変性を記述できる望みが持てる。この同値集合自身は、3.2節で注意を喚起しておいたように、そのドメインの共通性における不特定形(an anonymous form)になる。これは抽象の1種であるが、ソフトウェアファミリをその構成員の持つ共通性から形作る際に用いる抽象とは別物である。少し飛躍したアナロジではあるが、これは、自然界が共通の遺伝形質を共有する種を形成し、同じ種に属する各々の個体の可変性を管理している方法と似ている。
3.2節の該当箇所は以下のとおり。
共通性自身が可変性のヒントになることも多い。例えば、以下のような共通性を考えてみよう。
「
TextEditingBuffers
であればどのようなものでも、何らかのワーキングセットアルゴリズムを持つ」間違ってはいけない。これは共通性である。そして、可変性が存在する箇所を指し示している。
TextEditingBuffers
は、どのようなワーキングセットアルゴリズムを持つかという点で変化する。意味のある可変性は、大部分がこの手のカテゴリのものである。
ここではワーキングセットアルゴリズム
は同値集合
かつ不特定形
である。ステートマシンで考えると、「UserFSM
(共通性:集約した振る舞い、p.255)は、1つ以上のState
(不特定形)を持つ」ということになる。State
は共通性
であるが、可変性
を管理している。
3.4 ドメインと可変性レンジ 3.4.1 例:Text Editing Buffers (p.78)
可変性ドメイン集合は設計の「ツボ」である。適切なドメイン集合の組を可変パラメータに代入することにより、ソフトウェアの設計としてのファミリ構成員の生成を制御することができる。優れた設計であれば、プログラミング言語を用いてその共通性が適切に表現できるが、可変パラメータをどのように表現するかにも、設計の良し悪しがかかっている。
3.5 バインディング時期 3.5.4 バインディング時期の候補 (p.80)
ソースコードに埋め込まれる共通性と異なり、可変性はどこかのタイミング(時期)でバインディングしなければならない。
3.5 バインディング時期 3.5.5 例 (p.82)
可変性分析を実施する際に、各々の可変パラメータに対して、バインディング時期を把握しなくてはいけない。それによって、設計を活かす適切なプログラミング言語の要素を選択することができるだろう。
数多くの可変性を同値集合(equivalence)に抽象化することができるのであれば、少数の単純なパラメータですべての可変性を記述できる望みが持てる。
同値集合 = 感覚的にいうと「ほぼ同じような仲間たち」、数学的な定義は「反射律・対称律・推移律の3つを満たす関係」
確かに同値集合として可変性を抜き出せるのであれば、上手く抽象化できてるといえる。
3.6 デフォルト (p.82)
デフォルトは、可変パラメータがアプリケーションに影響を与えることがあまりない場合に有効である。
影響の大きなパラメータについては、アプリケーション側で明示的に指定する方が好ましいだろう。
私自身の経験では、リリース済みのソフトウェアに新しい可変点を追加した際に、後方互換性のためのデフォルトを用意することがよくある。
<?php
...
class Configuration implements ConfigurationInterface
{
...
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$treeBuilder->root('phpmentors_pageflower')
->children()
->arrayNode('conversation')
->addDefaultsIfNotSet()
->children()
->scalarNode('request_parameter_name')
->defaultValue('CONVERSATION_ID')
->cannotBeEmpty()
->end()
->scalarNode('session_storage_name')
->defaultValue('_pageflower_conversations')
->cannotBeEmpty()
->end()
->booleanNode('end_on_next_step_of_end_page')
->defaultFalse()
->end()
->end()
->end()
->end();
return $treeBuilder;
}
}
この例のブーリアン型のパラメーターend_on_next_step_of_end_page
は新たに追加したものであるが、デフォルトでこれまでどおりの動作をするような値(false
)を設定している。パラメーターrequest_parameter_name
およびsession_storage_name
のデフォルトは、_アプリケーションに影響を与えることがあまりない場合_に該当する。
3.6 デフォルト (p.83)
プロダクトコードがデバッキングコードほど頻繁に生成されないのであれば、デバッキング状態をオンにすることをデフォルトと
しないするほうがよいだろう。そのようにしたほうが、開発者の労力を減少させることができる。If we turn on the #define, then we can generate a production version. If production versions are much less frequent than debugging versions, then it's appropriate to leave debugging on in the default case. That makes less work for the developer.
3.6 デフォルト (p.82)
C++には、デフォルトを表現するのに役立つ言語要素が備えられている。例えば、デフォルト引数、基底クラスのメンバ関数、デフォルトテンプレートパラメータなどが有効である。C++では、共通性/可変性分析により把握したデフォルトが直接表現できることも多い。
私自身の最近の傾向としては、デフォルト引数はあまり使わずに、前述したようなドメイン特化言語レベルのデフォルトを使うことが多い。
3.7 可変性テーブル (p.83)
TextEditingBuffers
には、出力型、キャラクタセット、ワーキングセット管理技法、デバッキングコードの持たせ方というドメインにレンジがあることがわかる。このテーブルの列は、レンジを持つドメイン、そのレンジのバインディング時期、デフォルト値を表す。
可変パラメーター(可変性ドメイン)出力型
をデータベース
とした場合にできるアプリケーション、RCSファイル
とした場合にできるアプリケーション、…がドメインに対するレンジとなる。
3.8 可変性の落とし穴 (p.84)
2つのドメイン間に関係があるというだけでは、片方のドメインがもう一方のパラメータになるということにはならない。すべてのドメインが、可変パラメータの組に確実に関係したものになっていることが必要なのである。
負の可変性のソリューションで以下を検討してみたいです。
http://en.wikipedia.org/wiki/Policy-based_design
マルチパラダイムデザインの後に上梓された本でModern C++ Designで紹介されているパターン。#ifdefを使うよりもエレガントな解法なのかも。
6章で検討すべきテーマなのかもしれません。
3.11 要約 (p.87)
合理的理由があって負の可変性を導入する場合には、ドメインをサブドメインに分割すべきである。それができないときには、
#ifdef
といったあまり汎用的でないソリューションを考慮に入れることになる。
負の可変性のソリューションとして、#ifdef
やキャンセルを伴う継承
(参考: http://www.vincehuston.org/cpp/inheritance.html )が紹介されているが、要約によるとドメイン分割(例えばクラスの分割)によって負の可変性を消滅させることが推奨されているようだ。
3.3 正の可変性と負の可変性 3.3.2 負の可変性 (p.76)
strrecvfd
メッセージの使用の半分はKERNEL
であって、残りがそうでなはない場合には、fp
をSTRRECVFDの共通性ドメインの主要コンポーネントだと主張するのは難しい。代替案として、この構造を2つのドメインに分割することも考えられる。その場合にはおそらく、ファイル記述子のためのSTRRECVFDドメインと、ファイルポインタのためのSTRRECVFPドメインに分割することになるだろう。このように分割すると負の可変性は消滅することになる。
3.11 要約 (p.87)
ここでは、可変性に関連してドメインとそのレンジについて調べ、さらにバインディング時期、デフォルトについても考察してきた。これらは、可変性分析で最も重要となる特性項目である。可変性分析の結果をまとめたテーブルを用いることによって、このような特性を把握することができる。さらに、この分析構造に対応させて、C++コード構造を考え分析構造に合致させることも、このテーブルにより可能になる。
ドメイン駆動設計の重要な原則であるモデル駆動設計
は、問題ドメインの構造に合致した解決ドメインの構造を設計することであるといえる。