連載:[完全版]究極のC#プログラミング

Special Appendix 2
ステート集約プログラミング

川俣 晶
2010/07/05
Page1 Page2 Page3

 「ステート集約プログラミング」とは、筆者がうまく記述できたプログラムの特徴を抽出してまとめたものである。C# 2.0時代にまとめたものであるが、C# 3.0時代にもうまく機能し続けているので、以下に要約して掲載する。

現在のプログラミングの抱える課題とは何か?

 前提として、現在のプログラミングの抱える課題は次のとおりであると見なす。

  • 複雑さの爆発”によって、生産性が著しく落ちたり、場合によっては開発が進まなくなってしまう

 “複雑さの爆発”は、次の2つの要因によってもたらされると、ここでは考える。

  • コードの絶対量の拡大
  • 時間を経ることによって発生する要求の変化

“複雑さの爆発”とは何か?

 「ステート集約プログラミング」において、“複雑さの爆発”とは次のような状態であると定義する。

  • プログラム内部に存在する情報間の整合性が破れているが原因が明らかにできない、あるいは、これから行うコードの修正によって破れないという確信を持つことができない状態

 たとえば、処理しやすいように加工したデータと加工前の原データの整合性が破れ、加工済みデータから得られる結果と原データから得られる結果が食い違う……といった状況を示す。あるいは、処理開始時刻と現在時刻の差とそれまでの処理の累積時間が食い違うような状況を示す。

整合性の破綻を防ぐための方法

 整合性の破綻は、通常、プログラム内で「変化する情報」間で発生する。変化しない静的な情報に関しては、たいていの場合、容易に結果が予測可能であるため、整合性の破綻は発生しない。

 ここでは便宜上、変化する情報を「変数」という名前で呼ぶ。ここでいう「変数」には、アクセサなどで提供される「読みor/and書き」できる情報一般を含む。

 整合性の破綻を防ぐためには、特に“変数にのみ”注目した健全性維持の手法があればよいことになる。

 しかし、モジュール化プログラミングやオブジェクト指向プログラミングは、変数はモジュールやオブジェクトにバラバラに散在し、独立して更新される。たとえば、オブジェクトAに含まれる変数が更新されるとき、その値を整合性を取るべきオブジェクトBの変数も確実に同期して更新することを強制するメカニズムは存在しない。そもそも、オブジェクトAはオブジェクトBの存在を知らないという実装も珍しくはないので、整合性の破れを防ぐための手段は事実上ないに等しい。

 したがって、ここでは次のように結論付ける。

  • 静的な情報(手続きなど)と動的な情報(変数)を(モジュールやオブジェクトなどに)ひとまとめにして扱う手法には、まとめた単位をまたがる整合性の破綻を防ぐという観点が欠落している

 つまり、このような手法を「カプセル化」と呼ぶとすれば次のように結論付ける。

  • カプセル化は十分な効能を持たない*3

 したがって、ここでは「カプセル化」という戦略を放棄し、別の戦略を再構築しなければならない。

 整合性の破綻を回避するには、変数を一括して管理し、変数間の整合性をつねにチェックでき、あるいは整合性が破れないような更新参照メカニズムを提供する必要がある。

 そのためには、変数を1カ所に集約するのが最も簡単である。

 これが、「ステート集約プログラミング」の基本的な考え方である。

*3 ただし、これは大域的な観点の話であって、微視的な範囲に限れば、カプセル化は依然として有効ではないかと考えている。

「ステート集約プログラミング」の定義

 以上のような考え方から、「ステート集約プログラミング」は次のように定義される。なお、ここで用いられる「オブジェクト」という用語は、オブジェクト指向プログラミングのオブジェクトではなく、何らかの情報の入れ物を示すものとする。オブジェクト指向プログラミングのオブジェクト以外の入れ物を用いて実装してもよい。

  • 変化する情報をステート(状態)と呼ぶ
  • ステートは、限定された数の箇所に集約し、一括して扱う。これらを「状態オブジェクト」(ステートオブジェクト)と呼ぶ
  • プログラムの大多数の構成要素はステートを持たない。それらを「処理オブジェクト」(プロセッシングオブジェクト)と呼ぶ
  • 処理オブジェクトは、状態オブジェクトの状態を参照し、それによって処理を行い、状態オブジェクトを更新する処理を行う。処理オブジェクトの振る舞いは、状態オブジェクトによってのみ決定され、状態オブジェクトの内容が完全に同一であれば、完全に同じ結果にならなければならない(ユーザーの操作や通信といった処理は、状態オブジェクトの一種と見なす)
  • 状態オブジェクトは、自らに行われた更新要求に対して、整合性のチェックを行い、破綻していた場合は例外などを発生させ、要求を受け付けない
  • 処理オブジェクトは、状態オブジェクトから取得した状態に対応する処理を実行できない場合には例外などを発生させ、処理を中断する

「ステート集約プログラミング」によって得られるもの

 次のような効能が、「ステート集約プログラミング」によって得られると考えられる。

●整合性の破綻の即時検出と容易な対応が可能
  整合性の破綻は状態オブジェクトの例外によって、発生した瞬間に検出される。スタックトレースをたどれば、コードのどの部分が破綻させたのかすぐわかる。

●設計変更に強い
  状態オブジェクトが設計変更されない限り、どのようなプログラムの修正もすべて局所的なものになる。状態オブジェクトが設計変更された場合でも、整合性の破れはすぐに見つけられる。

●すでに書いたコードが無駄になりにくい
  同じことの反復になるが、プログラムの大半を占める処理オブジェクトの動作は状態オブジェクトにのみ依存するので、処理オブジェクトの修正は、他の処理オブジェクトへ影響しにくい。

●処理の書き足しが容易
  他の処理オブジェクトのことはいっさい知らなくても、状態オブジェクトの使い方だけわかれば処理を書き足せる。

「ステート集約プログラミング」とデータベース

 アプリケーションプログラムが、プログラム自身の中に永続的に保存される「変化する情報」を持つことなく、外部のデータベースのみに依存して動作する場合、これも一種の「ステート集約プログラミング」であると見なすことができる。そのように考えれば、「ステート集約プログラミング」とは昔から使われてきた定番のスタイルの1つでしかないともいえる。

 「ステート集約プログラミング」が提唱するのは、データベースとは関係ないプログラム中でも、同じような役割分担を実行しよう、という方法論である。

クラス設計パターン

 「ステート集約プログラミング」を行う際、筆者は以下のようなクラス命名を慣習的なパターンとして行っている。以下のクラスはすべて静的クラスであり、実体は1つしかない。

●Stateクラス
  基本となる状態オブジェクト。

●Flagsクラス
  単純な値で示される変化される情報を集めたコレクション。ユーザー設定の状態を提供するなどの役割を持つ。実装によりStateクラスのラッパであったり、状態オブジェクトの1つであったりする。

●Constantsクラス
  定数値を集めたもの。

●Generalクラス
  変化しない情報や機能のうち、プログラム内で共通して用いられるメソッドなどを集めたもの。処理オブジェクトの一種。

●(処理オブジェクト)
  一般の処理オブジェクトには決まった名前はない。自由に名前を付け、いくつでも好きなだけ作ってよい。

よくある質問

 : 固定値で変わらない商品名と日々変動する価格の情報を扱うプログラムを作成したいと思います。この場合、通常は、商品名と価格を持つ商品情報オブジェクトを作ると思いますが、「ステート集約プログラミング」ではどうなりますか?

 : 価格は状態オブジェクトに格納し、商品名は適当な場所(特に決まってはいない)に格納する。状態オブジェクトには商品名や商品IDなどから価格を読み出すメソッドなどを用意する。直感的にわかりにくいように思えるかもしれないが、実はこの構造が要求に対して最もシンプルな構造になる。

 : それでも、商品名と価格を持つ商品情報オブジェクトがほしいのですが。

 : 商品情報オブジェクトが存在し、それが価格を取得するメソッドを持つことはかまわない。ただし、そのメソッドは「価格を取得する」という機能を、状態オブジェクトに委譲しなければならない(商品情報オブジェクト自身は価格の値を保持しない)。

 : 直感に反してわかりにくいのでは?

 : 「ステート集約プログラミング」の効能は、直感でプログラム全体を把握できない規模にプログラムが膨れ上がった状態で発揮される。このような状態でコードの追加やトラブルシューティングを行うと違いがわかるだろう。

 : 僕の考えたやり方と「ステート集約プログラミング」はよく似ています。僕とあなたは同じ理想を求める仲間ですか?

 : 仲間ではない。君は自分の頭の中で考えた結果について述べているが、筆者は頭で考えた「正しさ」にはなんら価値を認めていない。それは、何も証明されていない単なるアイディアの域を出ていない。それに対して、筆者は“考えた”のではなく、実際に稼働するコードから“抽出した”のである。この差は決定的である。

 : 「ステート集約プログラミング」はあらゆる開発で有効ですか?

 : 答えはノーである。「銀の弾丸」が存在しない以上、「ステート集約プログラミング」も銀の弾丸ではありえない。「ステート集約プログラミング」とは、あなたが“カオスの縁で踊る”ステップのバリエーションを増やすものであって、他のステップがなくてもよいと主張するものではない。


 INDEX
  [完全版]究極のC#プログラミング
  Special Appendix/おわりに
    1.Special Appendix 1 現役C#プログラマーが語るC#を使いこなすツボ
  2.Special Appendix 2 ステート集約プログラミング
    3.おわりに ― C# 1.xから3.0への進化とは?
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間