データと情報の違い
データ(data)と情報(information)をソフトウェア開発論の中で区別して語ることはあまり多くないが、ただ取得したり計測したり外部から連携されてくるモノと、それを活用して事業活動で使う段階のモノには明らかな違いがあるので、これを区別して世の中の方法論を理解してみようという試み。
データと情報を区別している文献
データ: それ自体に意味はなく、記録として残しておくもの
情報: データを選択・加工して、当事者が必要とする知識(新たな価値)を取り出したもの
DIKWの論文。データと情報を区別する系の元ネタ。
データ: モノやイベントの性質を表すシンボル
情報: 処理されたデータであり、その処理はその有用性を高めることを目的としている
知識: 指示、つまりHow-toの質問に対する答えによって伝えられる
データ: シンボルやシグナルの読み取りの記録
情報: 意思決定や行動のための関連する意味、含意、インプットを含むメッセージ
知識: 情報を元に価値を創造する
ソフトウェアにおけるデータと情報の区別
主にLiewの定義を元に考える。
データを多くの文献で「ただ取得したり計測したり外部から連携されてくるモノ」とすると、スキーマ定義を与えたモノはデータなのか? という疑問が浮かぶ。データは構造を持っていても良いので、スキーマが定義されていても良いが、これが特定の意思決定や行動のためのものであるとデータとは言えず情報になる。
ハッキリした線引きはできないが、以下のように考えうる。
データを特定の意思決定や行動のために必要な形に解釈を加えたものが情報である。
Rich Hickeyはデータに下手に解釈を加えず、事実として記録されたものと捉えることで、必要な時に解釈を加えて価値を生み出す機会の最大化を主張しているようにみえる。
Alan Kayは解釈するものがいてはじめて利用価値がある(DIKWでいうところの知識が生み出せる)という立場から、データだけでは価値がない、と返しているようにみえる。
データと情報の間には明確な線引きはできないものの、概ね以下のようにDIKWと整合できる。
https://gyazo.com/c8454f73bcf97c017727aac4473f08d6
つまり、データを解釈したものがドメインモデルである、と考える。
オブジェクト指向とデータ
オブジェクト指向はデータと振る舞いをひとかたまりとするものである。振る舞いをデータにバンドルできるということは、データを解釈済みの情報として扱うことを前提としていることに他ならない。
情報ではなく単なる「データ」を扱うと貧血症と呼ばれる
単なる「データ」であるにもかかわらず、半ば無理やり振る舞いを持たせる
ふるまいが特定の1つの情報にバンドルさせるのが難しいことがある。
例えば口座間の送金で、「送金する」という振る舞いは送金元口座なのか送金先口座なのか? のような問題
解釈をどこでやるかのスタイル3選
1. データの解釈は各メソッドや関数の中で行う
すなわちドメインモデルを作らない。
code:task.ts
class Task {
title: string;
deadline: Date;
status: TaskStatus;
asignees: Person[];
completedAt: Date;
}
function postpone(task:Task, days: number) {
if (task.status != "DOING") {
throw new Error(延期するにはTaskがDOINGでなければなりません);
}
if (days <= 0) {
throw new Error(延期する日数は1以上の整数で指定してください);
}
task.deadline.setDate(task.deadline.getDate() + days);
}
一見、上記Taskはモデルとして機能しているようにみえるが、データと情報の区別の観点からいけば、Taskの解釈の仕方はいかようにもできる余地がある。そのため、Taskを処理する関数の中でその状態のチェックが入る。Taskのデータを期限延長という業務で見たときには、ステータスがDOINGでなくてはならないという制約がる。が、これをTaskが満たしているか解釈するのは期限延長の直前になる。すなわち、データを業務的な解釈を加え情報として利用する箇所が、あちこちに散らばるることになる。
さらには各プロセスの事前条件/事後条件がコードからは読み取れないので、ドキュメンテーションが重要になる。
2. データの解釈を関数の事前条件、事後条件として記述する
あまりないスタイルではあるが、「情報」の仕様変更影響が波及するのを最小限にするために、データのまま扱い、ロジック(知識)のIN/OUTの条件として、どう解釈するかを記述する。
1のスタイルで、事前条件/事後条件をドキュメンテーションとしてきっちり書きメンテしなきゃいけない点を、動くコードとして記述するところに違いがある。(この視点だけからすれば、Design by Contractと同じにみえる)
code:task.clj
(s/def :task/title string?)
(s/def :task/status #{:todo :doing :done}) (s/def :task/assignees coll?)
(s/def :task/completed-on #(instance? LocalDateTime %)) (s/def :task/task
(s/def :task/status-doing #(= (:task/status %) :doing)) (s/fdef postpone
:args (s/cat :task (s/and :task/task :task/status-doing)
:days pos-int?)
:ret :task/task)
(st/instrument `postpone)
(def task {:task/title "task"
:task/status :done
:task/deadline (LocalDateTime/now)})
このスタイルは、型に秘められた暗黙的な意味的結合を弱めることを目的とするが、その分コード記述量は増えがちである。 3. データの解釈は型として記述される
他の解釈の余地がない程度まで、型を設計すると、ドメインロジックからはバリデーションが消える。
code:task.ts
type TodoTask = {
title: string;
}
type DoingTask = {
title: string;
deadline: Date;
asignees: Person[];
}
type DoneTask = {
completedAt: Date;
}
type Task = TodoTask | DoingTask | DoneTask
function postpone(task: DoingTask, days: PosInt) {
task.deadline.setDate(task.deadline.getDate() + days);
}
このスタイルでは型が増えがちである。が、データの解釈は型に表現され、型がそのドキュメンテーションの役割を果たすようになる。
ここに至るためには、以下の性質を満たすようにドメインモデルを作っていく必要がある。