代替の計算モデルを実装するためにデータ構造内のコードブロックを整える。
メインストリームであるオブジェクト指向プログラミング言語はバランスがとれているが、特定の問題に特化していない。
適応モデル(Adaptive Model)は命令型言語に別の計算モデルを実装できる。
ソフトウェアは対象となる領域のモデルを構築するが、オブジェクトモデルの中でさえ処理フローはコードによって命令される。データの影響は処理の詳細では違いが出るが、大まかにはあまり変わらない。
特定のシステムを読み込む状態モデルによって、システム全体の振る舞いを大きく変化させる。状態モデルのインスタンス化はプログラムで行われる。ステートマシンの一般的なSemantic Modelであり、これは恒常的要因であり、ステートマシンが可能なことが何なのかに対する制約である。しかし本当の意味において実行するプログラムは、特定のステートマシンの形態(configuration)である。
(全訳)あるモデルがシステムの中で主な振る舞いの役割を担う時、私はそれを適応モデル(Adaptive Model)と呼びます。ソフトウェアでの境界と同様に能動的(active)なモデルと受動的(passive)なモデルとの境界は曖昧ですが、分類するには便利です。かつて適応モデルはデータとプログラムの境界を表しており非常に流動的でしたが、私たちは新たな可能性と問題の世界に進みます。いくつかのソフトウェアのコミュニティはこの世界を享受しています。Lispコミュニティはコードとデータの二重性を特に大切にしています。しかし多くの開発者にとっては、それは魅力と恐怖の両方を合わせもった世界です。
ここでのDSLの役割は、意図がよりはっきりと表せるよう言語を提供することで適応モデルをプログラムしやすくすることだ。適応モデルを使うにあたって最も難しいことの一つは、それが何をすることになっているかについてわかることであり、DSLはそれを克服する大きな助けとなる。
最初の例では適応モデルと命令コードが密接になっているので、より広い範囲を扱うアクションを使うか条件(ガード)を配置するかを行った方がよい。多くの場合、普段のプログラミング言語のコードに適応モデルのデータ構造を埋め込むことができる。
プロダクションルールシステムの例。ルールはboolean条件とアクションの2つ。クロージャを使うと自然に表せる。
rule.Condition = j => j.Start == "BOS"; rule.Action = j => j.Passenger.PostBonusMiles(2000);
クロージャは任意のコードブロックにデータ構造を簡単に埋め込めるが、多くの言語はクロージャ機能を持っていない。
最も簡単な対処方法はCommandパターンである。
class RuleWithCommand { public RuleCondition Condition { get; set; } public RuleAction Action { get; set; } public void Run(Journey j) { if (Condition.IsSatisfiedBy(j)) Action.Run(j); } } interface RuleCondition { bool IsSatisfiedBy(Journey j); } interface RuleAction { void Run(Journey j); }
サブクラスで特定のルールを記述する。
var rule = new RuleWithCommand(); rule.Condition = new BostonStart(); rule.Action = new PostTwoThousandBonusMiles(); class BostonStart : RuleCondition { public bool IsSatisfiedBy(Journey j) { return j.Start == "BOS"; } } class PostTwoThousandBonusMiles : RuleAction { public void Run(Journey j) { j.Passenger.PostBonusMiles(2000); } }
コマンドのパラメータ化によりサブクラスの記述量を減らせる。
var rule = new RuleWithCommand(); rule.Condition = new JourneyStartCondition("BOS"); rule.Action = new PostBonusMiles(2000); class JourneyStartCondition : RuleCondition { readonly string start; public JourneyStartCondition(string start) { this.start = start; } public bool IsSatisfiedBy(Journey j) { return j.Start == this.start; } } class PostBonusMiles : RuleAction { readonly int amount; public PostBonusMiles(int amount) { this.amount = amount; } public void Run(Journey j) { j.Passenger.PostBonusMiles(amount); } }
クロージャのない言語では、このように対処する。
別の方法として、メソッドの名前を使ってリフレクションで呼び出すことが挙げられるが、あまり好きではない。
DSLにてクロージャの表現力を最大に生かすには、内部DSLにクロージャまたは外部DSLにForeign Codeのどちらかを使う。
DSLは適応モデルにとって有用なツールであり、振る舞いをより明確にするプログラミング言語を使ってモデルをインスタンス化する。しかしモデルが複雑になると十分ではなく他のツールを使う必要がある。
モデルの実行時に何かしらトレースできるものを使うのは重要である。「このプログラム、なんでこんなことやってんだっけ?」に答えてくれる。
graphvizなどのツールを使ってモデルを視覚化したり異なる視点から見る種々のレポートを使うのも非常に有用である。
このような視覚化は言語ワークベンチの多重投影に似ているが、編集可能ではない。これらはビルドプロセスにより構築可能。
適応モデルを使うべき時はあなたが代替の計算モデルを使いたいと思った時であり、それが何時かに対する私の提案としては、異なる計算モデルに従って振る舞いを表現しようとしている時であり、プロトタイピングにて簡単になるかどうか検証してみることだ。
適応モデルは一般的な計算モデルを含んでいるが、他のパターン(Semantic Modelとか)も試す価値はある。フレームワークのように時間を経て成長し形成される。
適応モデルは非常に理解しづらいのが大きな欠点だ。
その難しさはアクティブなモデルが暗黙的な振る舞いをもたらすことに起因する。意図を明確に表したプログラムを書くのは難しいので、ツールを作ることで調査コストを下げることができる。
通常は適応モデルを理解する人が1人か2人いるので、有効に活用すると生産性が大いに向上する。
私は適応モデルをとても強力だと思っており、適応モデルによる生産性向上を実感している。しかし、ほとんどの開発者は得体の知れないものと思っているのも事実である。しかし時には誰も触りたがらないシステムを持つのは良くないので適応モデルに取り組まなければならない。そうしないと、そのシステムを誰もメンテナンスできなくなってしまう。
DSLはこの問題を軽減してくれる。DSLなしでは適応モデルをプログラムしたり何をしているのかを理解したりするのは難しい。DSLにより暗黙的な振る舞いをより明らかにしてくれる。DSLがよりメジャーになるとより多くの人が適応モデルに満足し、生産性の恩恵を感じてくれると思っている。