2013年1月7日月曜日

メッセージ・フロー図

システム・アーキテクチャなどを記述するモデル図を作成する際に困るのが、データ・フローとコントロール・フローの使い分けです。同期処理のみで構成される処理の場合は、データ・フローとコントロール・フローが一致するケースが多くなります。この場合、データ・フローまたはコントロール・フローのどちらか一方を書いておけば十分です。

しかしクラウド・アプリケーションのようにいたるところで非同期処理やバックグラウンド処理があるような場合、データ・フローとコントロール・フローが複雑に交錯することになりますが、両方のフローを線で記述していくと非常に煩雑な図になってしまいます。

この問題に対応するために考案したのがメッセージ・フロー図です。

SimpleModelingのメタモデルの最近の全体像は「MindmapModelingと集合知(7) - クラウド拡張」にありますが、メッセージ・フロー図は図の左側にあります。

2009年ごろから使っていますが、初期のものは「(仮称)クラウド研究会@札幌」にあるような形でした。

気づいたら文法を資料化しないまま随分経ってしまいましたが、2013年のはじめにひと通りまとめておきたいと思います。

文法

メッセージ・フロー図で使用するフロー線の文法です。



フロー線の先端部でプッシュ、プルの違いを表現します。また、白抜きの先端はデータフローとコントロールフローの向きが異なることを示します。

個々の詳細説明は後日。

使用例

前出の矢印を使ったロバストネス図は以下になります。



通常のロバストネス図はアクター、バウンダリ、コントロール、エンティティの各オブジェクト間の通信経路のみが分かりますが、メッセージフロー図の矢印記号を使うことで、(慣れれば)データ・フローとコントロール・フローをひと目で把握することができます。

2012年12月21日金曜日

ゆるふわScalaプログラミング・振り返り(7)

11月30日に開催されたBPStudy#63で「Scalaプログラミング・マニアックス」について、「ゆるふわScalaプログラミング」 といった方向性で振り返りをしています。その7はトレイトです。



トレイトの便利さをどのように紹介するのか考えがまとまらないうちに、当日となってしまったのでスライドは今ひとつ焦点の絞れていないものになってしまいました。

そういうこともあり、本ブログでもトレイトを元に以下の記事を書いてみましたが、プログラミング・モデルにおけるトレイトの本質にせまるという感じには至りませんでした。

トレイトには単に多重継承ができる、というだけでない"何か"があるんですよね。

ただ、Tipsとしては有効な内容と思いますので参考にはしていただけると思います。

トレイトについては、「Scala基礎勉強会」で聞いた:

の2つが強烈ですし、またその他定番としては:

もあります。このあたりを盛り込んだ内容にいずれ昇華させたいと考えています。

モデリング

Scalaプログラミングを通じて、トレイトには大いに触発されて、モデリングにも取り入れ、SimpleModelerにも組込みました。

実際に使ってみたところ、トレイトを上流の設計段階からモデル要素として積極的に使っていくアプローチはかなり有効ではないかと感じています。プログラミング言語側のScalaに受け口があるので、モデリングとプログラミングがシームレスにつながる点も重要です。

モデリングとトレイトというと、DCIのようなアプローチも提案されています。

こういった点も含めて、モデリング、プログラミングの双方からトレイトの活用用法、トレイトによって実現できるアーキテクチャ、プログラミング・モデルを探って行きたいと思います。

2012年12月20日木曜日

Scala Tips / トレイトと抽象クラス

クラスの多重継承の問題点は、クラスの継承関係が循環グラフになってしまった場合に、循環してかち合ってしまったクラスの扱いが非常に難しいということです。循環してかち合ってしまったクラスは一つにまとめたほうが良いのか、別管理にしておいた方が良いのか、用途によってどちらもあり得ます。これに付随して、初期化の順番やメソッド呼び出しの解決の順番など、プログラミングを複雑に解決が難しい難問があります。

こういった問題があるため、Javaでは多重継承はサポートしないという選択を行いました。また、「クラスの継承関係が循環グラフになる」という問題を持たないインタフェースという言語機能を用意して、多重継承的な用途を実装可能にしています。インタフェースは実装を持たないため、「実装部」が循環グラフになる問題が起きないわけです。

インタフェースは素晴らしい解決策でしたが、機能が絞りこまれているので用途がかなり限定されます。具体的には、実装の再利用の目的には使用できません。

トレイトは、実装部を持ちながら、継承関係の循環グラフ問題を以下の手法で回避しています。

  • コンストラクタを持たない。
  • 同じトレイトが複数指定されても、トレイト実装の実体は1つになる。

この制約をつけることで、ミックスインが可能になったのは非常に大きな成果ということができます。

一方、プログラミング時に意識しなければならないのは、トレイトにはコンストラクタがなく、コンストラクタを使っての初期化はできないということです。

この制約がどの程度のものなのか、「実装の再利用」とのトレードオフがペイしたものなのか、という点が気になりますね。

トレイトとケースクラス

以下のトレイトPartyと、このトレイトをミックスインしたケースクラスPerson, Companyの構造は頻出のパターンです。

トレイトPartで抽象変数として定義したnameを、ケースクラスPersonおよびCompanyの引数nameで"実装"しているため、このまま普通に動作します。子供(トレイトPersonやCompany)からコンストラクタ経由でパラメタを受け渡す必要はありません。

trait Party {
  val name: String
}

case class Person(name: String) extends Party
case class Company(name: String) extends Party

抽象クラスとケースクラス

これと同様のことを抽象クラスを用いて実装すると以下になります。

abstract class Party(val name: String) {
}

case class Person(n: String) extends Party(n)
case class Company(n: String) extends Party(n)

トレイト版と同じことができているだけですが、記述が少し冗長になります。特にパラメタをコンストラクタに積み直すところはプログラミング時にかなり面倒くさい部分です。

しかも、抽象クラス版では、抽象クラスであるために複数の親クラスを同時に継承、つまり多重継承を行うことができないという弱点があります。トレイト版が多重継承的なことが自在にできることと対照的ですね。

トレイト vs 抽象クラス

トレイトと抽象クラスの例を比べてみましたが、この例だけをみるとトレイトが圧倒的に便利という結論になります。

もちろん、親クラスに対して、コンストラクタで引数を渡して初期化を行うということが有用なケースも多々あるので、この極端な例だけを引き合いに出してトレイトが"圧倒的に便利"というわけにもいきません。

とはいえ、日々のScalaプログラミングの感想では、案外トレイトだけでも大丈夫なケースが多い、というのが実感です。特にイミュータブルオブジェクトの場合、コンストラクタでの初期化で複雑な処理は必要ないのが普通なので、トレイトで十分なケースが多いでしょう。

さらに、関数型プログラミング的な指向が強くなればなるほど、イミュータブルオブジェクトを多用するようになるので、トレイトの優位性が高まります。

そういうわけで、ボクが最近Scalaプログラミングをしながら着目しているのは、"抽象クラスでないといけない用途はなんだろう?"ということです。

トレイトではなく、抽象クラスでないといけない用途が明確になれば、日々のプログラミングで迷いなく抽象クラスとトレイトの選択を行うことができます。最近は、まずトレイトで実装してみて、抽象クラスでないといけないと分かった時に、抽象クラスに変更する、というアプローチをとっています。その結果、あまり抽象クラスに変換するケースはなく、案外トレイトで大丈夫という感触を得ています。

諸元

  • Scala 2.9.2

2012年12月19日水曜日

Scala Tips / トレイトでクラス分割

Scalaプログラミングの便利さを日々体感しているわけですが、その重要な要素の一つがトレイト。トレイトは関数型言語のものではなく、オブジェクト指向プログラミングの技術ですが、そのあるなしはプログラミング・モデルに大きく影響します。

Scalaにおけるクラス設計は、このトレイトの存在だけを意識するとしてもJavaのそれとは別ものということができます。

トレイトには色々な用途がありますが、最近ボクが地味に多用しているのがクラスの分割です。機能数が増えて大きくなったクラスを幾つかのトレイトに分割するだけなのですが、プログラムの見通しがよくなり、また機能間の依存関係も明確になるので、大変重宝しています。

クラス分割の例

以下のクラスがあるとします。変数aに設定した値をメソッドbとメソッドcが使用しています。

class Whole {
  val a = 1
  def b = a + 1
  def c = a + 2
}

このクラスを以下の3つの部品(クラス+トレイト)に分解し、これらの部品を組み立ててクラスを定義します。

Whole
クラス本体。変数aを定義している。
Parta
部品となるトレイト。変数aを使用するメソッドbを定義している。
Partb
部品となるトレイト。変数aを使用するメソッドcを定義している。
class Whole extends Parta with Partb {
  val a = 1
}

trait Parta {
  self: Whole =>

  def b = a + 1
}

trait Partb {
  self: Whole =>

  def c = a + 2
}

ポイントとなるのがPartaのメソッドbとPartbのメソッドcがクラスWholeの変数aに依存していること。普通はこういう場合、クラスWholeとトレイトb, cはインヘリタンスの関係が必要になってきます。

しかし、この場合はトレイトPartaとPartb内のself-type annotationである「self: Whole =>」によって、トレイトがWholeにmixinされることが保証されるためクラスWholeの変数aを使用することができます。

そしてクラスWholeの定義で「class Whole extends Parta with Partb」とすることで、クラスWholeにトレイトPartaとPartbの機能を追加することができます。

この場合、トレイトPartaとトレイトPartbの間には依存関係がないことが明確に分かります。このような形で、機能間の依存関係をできるだけ疎にしていくのが、大きなプログラムを作るときのコツとなります。

実行

実行結果は以下のとおりです。普通に動作しました。

scala> val w = new Whole
w: Whole = Whole@2bf75442

scala> w.a
res0: Int = 1

scala> w.b
res1: Int = 2

scala> w.c
res2: Int = 3

諸元

  • Scala 2.9.2

2012年12月18日火曜日

SimpleModeler 0.4.0

モデルコンパイラSimpleModeler 0.4.0をリリースしました。

マインドマップ(XMind)、SmartDox DSL、CSVからクラス図とJavaプログラムを生成する機能が実用フェーズとなっています。

最近拡張した文法が動作します。

それそろ安定してきたのでバージョン0.4.0とします。

機能

Simplemodeler 0.4.0では以下のオプションを提供しています。

オプション機能状況
projectプロジェクト生成α
importモデル移入α
convertモデル変換試験的
html仕様書生成α
javaJava生成
androidAndroid生成α
diagramクラス図生成
buildプロジェクトビルド試験的
gaejGoogle App Engine Java生成試験的
gaeGoogle App Engine Python生成試験的
gaeoGoogle App Engine Oil生成削除予定
grailsGrails生成試験的
g3g3生成試験的
asakusaAsakusa生成試験的

基本的にはマインドマップ(XMind)、SmartDox DSLとCSVからクラス図とJavaプログラムを生成する処理が実用フェーズになっています。その他の機能はα版または試験的実装の状態です。

インストール

プログラムの配布は、Scala用のプログラム配布ツールconscriptを使っています。

conscriptをインストールした後、以下のようにしてSimpleModelerをインストールします。

$ cs asami/simplemodeler

以下のコマンドがインストールされます。

sm
SimpleModelerコマンド
$ sm -version
Copyright(c) 2008-2012 ASAMI, Tomoharu. All rights reserved.
SimpleModeler Version 0.4.0 (20121217)
graphviz

-diagramオプションでクラス図を生成する場合は、graphvizのインストールが必要です。graphvizのインストール方法は以下を参照してください。

各プラットフォーム向けパッケージ管理ツールでもインストールできるようです。

Mac
http://www.macports.org/
Windows
http://sourceware.org/cygwinports/

使い方

マニュアルはまだありません。以前のバージョン用のものがありますが、機能が色々変わってしまったので一から見直す予定です。

リファレンスマニュアルとユーザーガイドの元ネタをこのブログで随時書いていきます。

CSV

Mind(マインドマップ)、SmartDox DSL、CSVからクラス図を生成することができます。

以下のCSVファイルをsample.csvとして用意します。

#actor,base
顧客
個人顧客,顧客
法人顧客,顧客
#resource,attrs,powers
商品,商品名;定価(long),商品区分(第1類;第2類;第3類)
#event,parts
購入する,顧客;商品

SimpleModelerを以下のように実行します。

$ sm -diagram sample.csv

以下のクラス図の画像が生成されます。

SmartDox

以下のSmartDoxファイルをsample.orgとして用意します。

#+title: Table

SmartDox DSLを使って記述したモデルの
サンプル文書です。

文書でサンプルモデルの定義をします。
本来は文書中の仕様記述の文書は
定義するモデルに対するものになります。

しかし、この文書ではSmartDox DSLの記述例として
SmartDox DSL文法の説明を記述することにします。

* サンプル文書の目的

このサンプル文書は表を中心にしてクラス定義するサンプルです。

登場人物、道具、出来事の各エンティティの種別の下に
顧客、商品、購入といった具象エンティティを節として
定義します。

そして、それらの節の下に属性一覧または特性一覧として
エンティティの属性や関連を記述していきます。

* 登場人物

** 顧客

IDの指定はありませんが、以下のルールで推測しています。

- 陽にID指定がない場合、先頭の属性の属性名が「id」(大文字可)で終わる場合はIDとみなす。

#+caption: 属性一覧
| 名前   | 型     | カラム  | SQL型        |
|--------+--------+---------+--------------|
| 顧客ID | token  | ID      | CHAR(16)     |
| 名前   | token  | NAME    | VARCHAR(64)  |
| 住所   | string | ADDRESS | VARCHAR(256) |

* 道具

** 商品

IDは、ID欄で指定しています。

#+caption: 属性一覧
| 名前   | 型    | ID | カラム | SQL型       |
|--------+-------+----+--------+-------------|
| 商品ID | token | ○ | ID     | CHAR(16)    |
| 名前   | token |    | NAME   | VARCHAR(32) |
| 定価   | money |    | PRICE  | LONG        |

* 出来事

** 購入

IDは、特性欄で指定しています。

#+caption: 特性一覧
| 特性 | 名前   | 型    | 多重度 | 派生        | カラム      | SQL型    |
|------+--------+-------+--------+-------------+-------------+----------|
| ID   | 購入ID | token |        |             | ID          | CHAR(16) |
| 属性 | 日付   | date  |        |             | DATE        | DATE     |
| 関連 | 顧客   | 顧客  |      1 |             | CUSTOMER_ID | CHAR(16) |
| 属性 | 顧客名 | token |        | 顧客.名前   |             |          |
| 関連 | 商品   | 商品  |      1 |             | GOOD_ID     | CHAR(16) |
| 属性 | 数量   | int   |        |             | AMOUNT      | INT      |
| 属性 | 商品名 | token |        | 商品.名前   |             |          |
| 属性 | 単価   | money |        | 商品.定価   |             |          |
| 属性 | 総額   | money |        | 数量 * 単価 |             |          |

SimpleModelerを以下のように実行します。

$ sm -diagram sample.org

以下のクラス図の画像が生成されます。

2012年12月17日月曜日

MindmapModeling「チャネルの多様化で難題と化す予算の最適配分」

12月15日(土)に横浜モデリング勉強会(facebook group)を行いました。また、会場には(株)アットウェア様の会議室をお借りしました。参加された皆さん、アットウェア様、どうもありがとうございました。

この勉強会で、浅海が作成したモデルを紹介します。モデルはMindmapModelingの手法で作成しました。(勉強会で使用したチュートリアル)

ワークショップの流れ

モデリング勉強会はワークショップ形式で以下の作業を行います。

  • 雑誌記事から情報システムの企画書、提案書、RFPの元ネタとなるモデルを作成する。

その上で、「要求仕様確認、実装可能性確認、開発のベースとなるプログラムを自動生成するモデルを目指」します。詳細は「ワークショップの進め方 第2版」になります。

テーマ

モデリングの対象は、日経ビジネス誌の記事「チャネルの多様化で難題と化す予算の最適配分」です。

用語の収集と整理

まず用語の収集と整理します。

MindmapModelingに慣れてくると、用語がだいたいどこの枝に収まるのかわかるようになるので、用語を拾いながらラフなモデルを作っていきます。




今回の記事は、広告の効果を計測するメカニズム的なことが多く書かれていたので、このあたりを規則と登場人物を中心に分類しました。

クラス図

この段階でのマインドマップをSimpleModelerでクラス図化したものが以下になります。




いくつかモデルの島ができていますが、全体としてはまだばらばらです。

物語

次の作業は「物語」です。

モデルは中心軸がないと単なる「用語」の集りなのでまとまりがでてきません。何らかの目的を実現するための構造を抽出したいわけですが、この「目的」と「目的を実現するための構造」を掬いとるためのツールとして有効なのが「物語」です。オブジェクト・モデリングの概念ではビジネス・ユースケースということになります。

「物語」を中心軸と定め、「物語」のスコープで用語を取捨選択、組織化し、足りない用語を補っていきます。

その手順は:

  1. 物語の名前をつける。目的(goal)が明確になる名前がよい。
  2. 物語の主人公、相手役、脇役などの登場人物を定める。
  3. 物語で使用する道具を定める。
  4. 出来事または脚本の列として脚本を記述する。

となります。2の登場人物と3の道具は最初から完全なものはできないので暫定的なものを定め、4の脚本の作業を通して洗練させていきます。




「物語」として、「広告予算編成を最適化する」を設定し、この「物語」の作成を軸に、「出来事」の整理、「道具」の整理を進めました。

物語「広告予算編成を最適化する」は以下の出来事による脚本から構成されます。

  • マーケッティング施策を実行する
  • マーケッティング施策の結果を計測する
  • マーケッティング施策の結果を分析する
  • 予算配分する

さらに、各出来事を詳細化して、規則や道具を結びつけていきます。

今回の記事は内容がかなり絞りこまれていたので、モデリングの方向を問題領域の世界を記述するというよりシステム化を指向した詳細なモデルを記述するようにしてみました。

クラス図

この段階でのマインドマップをSimpleModelerでクラス図化したものが以下になります。




かなりクラス図らしくなってきました。ビジネス・ユースケース→イベント→アクター+リソースの流れが分かりやすく可視化されています。

ここまでの作業で時間切れとなりました。

ちょっと洗練

時間内に作ったマインドマップモデル+クラス図はSimpleModelerの機能不足もあって、出来事から規則に続く流れがクラス図上に表示できませんでした。

そこで、SimpleModelerに機能追加を行いながら、この点の記述の精度を上げたモデルが以下のものです。




クラス図

クラス図は以下になります。




SimpleModelingではアルゴリズムを記述するためのモデルとして、規則に加えてサービスを持っていますが、今回このサービスをマインドマップモデルでも使ってみました。

SimpleModelerで出来事から規則およびサービスへの関係は依存性(dependency)として記述しています。

ノート

オブジェクト・モデリングの鍵の一つは、静的構造を記述するドメイン・モデル(クラス図)と動的モデルをどのように関連付けていくのかという点です。

SimpleModelingでは、イベント(出来事)を起点にルールやサービスを呼び出すという点を軸の一つにしています。今回はよい機会なのでSimpleModelerにこのあたりの機能を追加してMindmapModelingからクラス図の生成の処理に盛りこんでみました。

具体的な記述方法、使い方は追々ブログで紹介していきたいと思います。

次回

次回はまだ確定していませんが1月19日(土)を予定しています。

詳細情報はfacebookグループ「横浜モデリング勉強会」を参照してください。

今回と同じく「ワークショップの進め方 第2版」の手順で、「雑誌記事から情報システムの企画書、提案書、RFPの元ネタとなるモデルを作成する」を行う予定です。

2012年12月14日金曜日

Scala Tips / Streamで採番(2)

Streamで採番では、Streamのfromメソッドを使って数値の連番による番号の採番を行いました。

場合によっては、採番する番号として「A00001」というような英数字と数字の組合せを取りたい場合があります。

このような場合は、カスタムでStreamを作ることになります。

といっても、これは簡単で以下のような再帰関数のイディオムを使います。

def nums(i: Int): Stream[String] = {
  "A%05d".format(i) #:: nums(i + 1)
}

使ってみる

それでは新しく作ったnums関数による採番用Streamを使ってみましょう。

採番した値の型はStringになるのNumberedNameを修正します。

case class NumberedName(name: String, number: String)

シーケンスに対して採番を行い値と採番した番号のTupleを返す関数は以下になります。Stream#fromの代わりにnums関数を使っています。

def f(names: Seq[String]): Seq[NumberedName] = {
  for ((n, c) <- names zip nums(1)) yield {
    NumberedName(n, c)
  }
}

実行

それでは実行してみましょう。

まずテスト用のListを定義します。

scala> val xs = List("a", "b", "c")
xs: List[java.lang.String] = List(a, b, c)

このListに対する関数fの実行結果は以下になります。

scala> f(xs)
res6: Seq[NumberedName] = List(NumberedName(a,A00001), NumberedName(b,A00002), NumberedName(c,A00003))

ごくごく簡単に使うことができますね。

ノート

再帰関数とStreamを使うことで、採番ロジックを関数として部品化することができました。この部品はSeqのzip関数でシーケンスと結びつけることができます。

こういった疎結合の関数部品を用意して、関数合成のテクニックで組合せて使うのが、関数型らしいプログラミングです。Streamの活用はそのような関数型プログラミングを行う上で重要なテクニックになっています。

諸元

  • Scala 2.9.2