Hatena::ブログ(Diary)

Fly me to the Juno! このページをアンテナに追加 RSSフィード

2010-08-27

モジュール指向勉強会でソースコードリーディングを行いました。

先日8/23にDevLOVEモジュール指向勉強会が開催され、一コマ担当しました。その時の資料を公開します。

資料の中でソースコードリーディング前に、モジュール化の必要性について話しました。ぶっちゃけていうと、5人で半年の開発案件くらいだと、複数のモジュールに分割する必要なんかないです。日本のソフトウェア開発プロジェクトの少なくとも5割は5人プロジェクトらしいので、すぐにモジュール化を求めているプロジェクトはそんなに多くないんじゃないか、と思ってます。

ただ、そうはいっても世界に視野を移すと、順調にソースコードが増え、システムがどんどん複雑になっています。そのため、アプリケーション層のモジュール化が求められています。これはJavaに限った話ではなく、他の言語、Rails3や、RequireJSとJuicerなんかでもモジュール化を向上する取り組みが行われています。もっと言うと、他の分野のシステムでもモジュール化が進められています。複雑さを軽減させる枠組みとして、分割統治を採用するのは、コンピュータの世界ではよく採用されています。モジュール化はその方針でとりうる一つの形なのです。

さて、実際のソースコードリーディングですが、今回はJAMCircleの中の、ボード定義のエクスポート/インポートを行うクラスを題材にしました。このクラスは、利用ケースがそれほど多くない、と思ったので、TDDでクラスを書き始め、気づいたら一つのクラスとするにはちょっと大きくなりすぎたクラスです。考えようによっては別のモジュールにクラスを移すべきだと感じていました。なので、今回題材に取り上げ、どうやってモジュール分割していくのか、体験する事を題材に進めました。

資料の見方や、ソースコードについて説明し、やってみると、結構みんな手が動きません。後々聞いてみると、構造や振る舞いだけではなく、実際にどう使われるのか、ユースケースを最初に話を聞きたかった、という声が多く有りました。説明が不足してすいませんでした。分からないなりにも手を動かしてみると、解決への糸口に気づく事が有ります。なので、分からない事に気づくため、とりあえず手を動かし、なんでもいいから書いてみるとよいでしょう。

ソースコードを読んだ後、みなでどういう構造にしていったらいいか、意見を交わしました。結構違う視点の話が出てきて面白かったです。出力形式を拡張しやすいよう、ZipFileを継承してみるのはどうだろう、とか、VisitorパターンとStrategyパターンを採用してみるのはどうだろう、とか、他の人の視点がみられて良かったです。

質問への答えをつらつらと書いておこう

[twitter:@nobusue]さんのエンタープライズOSGiのセッションであった質問に対し、自分なりに答えたい事がいくつかありました。当日は[twitter:@nobusue]さんのセッションだったので、首を突っ込むのもなんだかな、と思ったんですが、改めて考えると、知っていたり、やっている事はいろいろあるので、ここで述べさせて頂きます。

モジュールの開発の権限を開発者ごとに分けたい

誰でもモジュールが触れる状態だと、既存のモジュールに意図しないコードが混入し、モジュールの情報隠蔽が行えず、結局うまくモジュール化を保てないのでは?という質問がありました。そもそもソースコードとモジュールは切り離せるので、開発するモジュールとリポジトリを別々にすることもできるし、モジュールに対してコード署名をつけてあげれば改変できないので、解決できると思った。Eclipseの例でまた恐縮なんだけど、最近Eclipseがリリースしているプラグインにはコード署名が着いていて、改変してもそのまま使えない形になってます。コード署名をつける話は、OSGi SpecのSecurity Layerで書いてあったようななかったような。

開発したモジュールはどうやって共有するの?

Mavenを使っているのであれば、Maven RepositoryにデプロイするだけでOK。無理にOBRを使う必要はありません。EclipseのPDEでBundle開発しているのであれば、p2を使うのも手。p2は出来る事がたくさんあるので、若干敷居が高い気がしますが、一度共有リポジトリを立てると、開発時に不足しているパッケージがあれば、リポジトリから検索してインストールする機構がEclipseにあるので多少楽できるでしょう。

2010-08-22

モジュラリティを考える

ソースコードの分かりやすさは、「単一責務の原則」と「関心毎の分離」により適切に構造を分割した、バランスのいいところにあると前のエントリに書いた。では、そこにモジュラリティが加わるとどうなるだろうか。

モジュラリティとは、システムを幾つかのモジュール*1に分離し、さらに分離したモジュールを組み合わせて新しいシステムを組むソフトウェア開発法、とここでは定義する。分かりやすい例はEclipseだ。例えばPyDevを導入すればPythonの開発環境が手に入る。このように言語環境ごとに作られたプラグインを導入すれば、それぞれの言語で開発ができるIDEが構築できる。EclipseはIDEとして必要な機能の全てをプラグインという形のモジュールに分離し、組み合わせる事でモジュラリティを実現している。*2

モジュラリティの利点と欠点を上げてみよう。

利点

モジュール毎に独立して開発できる

モジュールに分割できるのであれが、独立して開発ができるため、それぞれのモジュール毎に開発チームを分離し、開発を進める事ができる。システム内の知識を共有するのであれば、そのシステム全体においてソースコードが読める状態である事が望ましいが、その全てを読んで理解しきれるエンジニアはそう多くないだろう。エンジニアが開発しているモジュールのソースコードが読むべき範囲が限定され、明示されていれば、新規に参加する場合でも参加しやすい。

また、独立しているので、JavaであればJARなど、代替できるものがあれば、ビルドにはシステム全体のソースコードがなくてもコンパイルできる。要するにソースコードの公開範囲を調整できる。例えばライセンス管理など、重要な部分のソースコードを公開する事なく、それ以外のソースは公開する、と言う事も可能だ。*3

言い換えれば、システムをモジュール毎に分割統治されている、と言う事だ。

モジュール毎に責務を割り当てられる

モジュール毎に責務が割り当てられるので、うまく関心毎に分離できていれば、システムがどういう要素で構成されているのか、分かりやすい。モジュールに分離できるべき関心毎については後で述べる。

新規開発する機能をモジュールに分離できる

新しい機能は一度実装すればOKと言う事はほとんどない。作ったものを使ってみて、調整する必要があるので、たくさん変更されるし、もし作ってみて微妙な機能であれば、前の状態に戻せる事が望ましい。別のモジュールに分離して開発すれば、簡単に破棄できるので、プロトタイプ実装と言った実験的な機能を試しやすい。また、既存のソースを書き換える部分も同一モジュールとして開発を進めるよりも限定される。

ソースコードの変更による影響の予測が立てやすい

モノシリック*4なシステムでは、変更したソースコードによる影響がどこまで波及するか、予測を立てづらいためソフトウェア全体のテストが毎度必要だった。しかし、ソースコードを変更していないモジュールには、機能に変更がない事は自明だ。*5そのため、毎回ユニットテストを行う必要がない。(ただし、システム全体の統合テストは毎回実施した方がよいだろう。)

他のシステムへモジュールを再利用できる

開発したモジュールは、次第に安定し、特殊な用途でなければ他のシステムへ転用をしたくなる事があるだろう。モノシリックなシステムでは、その部分のみ分離した後の管理が非常に面倒な事になるが、元々モジュールに分離され、適切にバージョンの運用がなされていれば、他のシステムに転用後も、時期を見て新しいバージョンへ更新できるだろう。

欠点

アプリケーションとしての構造が複雑に

モジュールに分離すると、それぞれのモジュールは元のシステムより小さいので単純になるが、アプリケーションとしての構造は複雑になる。なぜならば、必要なモジュールが不足していたり、モジュールの組み合わせや、モジュールフレームワークへ登録された順番によって不具合がおこるなど、モノシリックなシステムだった頃は起きない障害に悩まされる事がある。出来るだけ他のモジュールへ依存しないように設計したり、組み合わせを考えたりする必要がある。

また、システムの構成を任意に変更できるため、システムの開発者や設計者が意図していない機能が追加される恐れがある。要するにうまく作らなければ脆弱性がシステムに同梱してしまうのだ。

新たなモジュールが必要になった時や、モジュールの変更時になんらかの作業が必要になる。

大した時間ではないが、モノシリックなシステムと比べると、ひと手間かかる。特にモジュールの変更時に適切にバージョンを書き換える必要がある。Maven2などのツールを用いれば、自動で更新する運用ができるが、リリース時にバージョンを固定するなど、やっぱりひと手間かかる。

以上をまとめておく。

利点

  • モジュール毎に独立して開発できる。
    • モジュールを開発チーム毎に担当できる
    • 開発時に理解すべきソースコード量を縮小できる
    • モジュールごとのソースを非公開にできる。
      • ライセンス管理などをモジュールに分離→重要な部分のソースを隠蔽しつつ、それ以外のソースを公開できる
  • モジュール毎に責務を作成できる。
    • モジュール毎に関心毎を分離できる、と言い換えてもいい。
  • 新規に開発している部分をモジュールに分離して開発できる
    • 実験的な機能を試しやすく、実装した機能が微妙だった場合に捨てるのが簡単
      • プロトタイプの実装と、ドッグフード的な導入が楽
    • 既存のコードへの変更を最小限に限定できる
  • ソースコードの変更による影響の予測が立てやすい
    • 変更していないモジュールのユニットテストを行わなくてもよい。*6
  • 他のシステムへのモジュールの再利用ができる

欠点

  • 個々のモジュールは責務毎に分割できるので、単純にできるが、アプリケーションとしての構造は複雑になる。
    • モジュールの依存関係が不十分で動作しない場合などの状況が生まれうる
    • 他のモジュールから自由に機能を追加できるようにも作成できるので、モジュールの組み合わせや、登録順序により、副作用が生まれ、不具合がおこる事がある。
    • モジュールの導入により、任意に機能を追加できるため、脆弱性を生みやすい
  • (大した時間ではないが、)モジュールの新規作成分、手間がかかる。

システムをモジュールに分割する場合、モジュールが持つ責務(関心)をどうやって分割するかが鍵になる。以下にどういう責務があるか、例をあげる。*7

  • 関心毎にシステムが持つ機能を分割
  • 他のシステムでも使い回せる汎用的なサービス
    • システム内を操作するシェルサービス
    • HTTPサービス
    • スクリプト言語サービス
    • 拡張機構(extender)
      • DIを行うサービス
      • モジュール内の設定ファイルを読み、システムを拡張する機構
  • モジュールの成長(変更)頻度の多さ(例:UI部と、それ以外)
  • テスト容易性が確保できる単位で分割(システム境界)

大体の責務の大きさが伝わると幸いだ。

*1コンポーネントと呼んでも良い

*2:基盤のモジュールフレームワークにはEclipseの場合はOSGi実装を利用しているが、モジュールフレームワークも含めて全てのプラグインがモジュールだ。

*3:これは既存のシステムでも、もちろん同等の事ができるが、分離されている分よりやりやすい。

*4:モジュールに分割せず、一枚岩なソフトウェア開発の事

*5:不安であれば毎回ユニットテストを行っても良いが、それにより失敗してしまうテストがあるのであれば、そのモジュールが変更したモジュールから影響されている事が分かり、モジュールとして望ましくない状態である事がわかるので、たまに実施してもよい。

*6:不安な場合はもちろん行っても良いが、テストを行って他のモジュールの変更による影響を受けたテストは、ユニットテストとしてあまり良くないテストと言える。あくまでユニットテストは対象クラスのロジックのみをテストすべき。

*7:特にOSGi環境においては、既に提供された有益なものを列挙