モジュール分割とタスク分割 - 誰のためのデザイン?

困難は分割せよ
方法序説』 - デカルト

ソフトウェアの設計上重要な二つの要素、モジュール分割とタスク分割。その目的と必要性、効果とともに、初心者が陥りやすい過度な分割による弊害について書いてみたいと思います。

モジュール分割は誰のため?

まず「モジュール分割」ですが、その目的は、ソフトウェアの意味的、機能的な単位に分割することにより、可読性や再利用性、あるいは、複数人開発での分担効率をあげようというもの。ソフトウェア設計における最も基本的な部分であり、最終的な、成果物の品質を左右する重要な設計要素ですね。

さて、モジュール分割でググると、STS分割技法、TR分割技法などの技法を紹介するページがあたります。
例えば以下あたりはまとまっていて参考になります。 私の場合、基本組み込み開発の世界にいますが、開発の現場でこれら単語を聞くことは少ない気がします。組み込みソフトの開発が大規模化してきたあたりから、組み込みでもオブジェクト指向設計が浸透しているので、最近の「オブジェクト指向ネイティブ」な世代には、クラス設計とか、デザインパターンから入った人が多いからかもしれません。そういう方には以下記事が、構造化プログラミングを経てオブジェクト指向に至る歴史がよくわかっておすすめです。

さて、前振りはここまでにして、今回の主題に入りたいと思います。それは、モジュール分割はそのソフトウェアを搭載した製品やサービスのためのものではないという点です。モジュール分割の意義は、
  • 開発者のため : ソフトウェアの可読性、分業効率の向上
  • 部品の再利用のため : 成果物の一部(又は全て)を使った派生開発
にあります。

そうはいっても、実際は、ソフトウェアの品質は、それを作る「人」に依存しますので、開発効率をあげることが、プログラムの方が品質に直結するので、上の話は極論ですが、何のためかという観点は非常に重要です。この考えに立てば、「成果物として何をつくるか」よりも、「誰が作るか、どうやって作るか」、そして、「成果物の再利用のスコープはどこまでか」が、モジュール設計を考える上で重要になるからです。

なぜこんなことを強調するのかというと、ソフトウェアのモジュール設計を見ていると、過度なモジュール分割がなされているケースが多いように思うからです。 4788514346 もちろん、ソフトウェアの部品化や階層化は、再利用性という観点で非常に重要な観点なのですが、全てのプログラムがそれらを考慮しないといけないというわけではありません。むしろ、再利用性を全く必要としない部分について、過度なモジュール分割を行って、厳密なインターフェースを定義していくことで、開発者が増えたり、必要以上のドキュメントやテストが増えることになりかねません。内部設計としてきちんとモジュール化するのはもちろん必要ですが、そこを外部公開してしまったとたん、害の方が多くなることがあるので注意が必要です。また、拡張性が全く必要ないとわかっているようなケースで、過剰なフレームワークを作ったりするのも、同じく害しかありません。

本来のスコープを超えた過剰設計は、百害あって一利なしです。

タスク分割は何のため?

もう一つのテーマであるタスク分割について。モジュール分割が静的な分割であるのに対し、こちらはプログラムの動的なふるまいを決定する上で重要な要素です。タスク分割というと、uITRONや VxWorksのようなRTOSの設計がイメージされますが、Linux等の非リアルタイムシステムにおけるプロセル分割、スレッド分割でも同じような考え方は必要です。(※ 厳密にはメモリ空間の扱いとか、リアルタイム性の保証の話とかで、違う点はありますが。)
タスク分割の動機は大きく分けると以下二点になります。
  • (1) モジュール化のための分割
  • (2) 並列性、リアルタイム処理のための分割

(1)の方は、先のモジュール分割の延長ですね。その機能毎にタスクを割り当てようというものです。タスク分割にとっては(2)の方が本質で、基本的には独立した実行コンテキストをもって、並列処理させることが主な目的です。(2)をもう少し細かくわけると、
  • 処理要求の間隔とそれに対する処理時間の吸収のため、処理タスクをわける
    (例) 入力デバイスからのイベント受付と、それをキューイングして順次処理するタスクをわける
  • 互いに独立した処理の並列実行
    (例) 複数クライアント毎に処理タスクをわける
  • リアルタイム性の高い処理の優先実行
    (例) 緊急度の高いタスクに高優先度を割り当てて、優先的に割り込み処理させる。
あたりでしょうか。これらに該当しない処理は、実行時の要求事項という観点からは特にタスクにわける必要がないので、あるとすれば、(1)モジュール化のための分割、ということになります。

確かに、機能単位にタスクを分けるのは、ソフトウェア構造をわかりやすくする上で有用なことも多いのですが、これが過剰すぎる場合があります。分割された各タスクが各々完全独立、依存関係なく並列動作できるものなら問題ないですが、これが一連の手続きの前後、あるいは、上下関係出会ったりする場合が問題です。タスク分割するということは、タスク間のやりとりは、関数コールではなく、キューなりイベントフラグなりのメッセージになっているはずですが、この時のタスク間の機能呼び出しは、同期型のものもあれば、非同期のものもあるでしょう。ここでいう同期型は、要求メッセージを投げた後、結果応答があるまで、イベント待ちで要求元タスクを待ち状態にさせるというものを指しています。これって、単なる関数コールと何が違うのでしょうか?一件、独立性が確保されて、可読性があがったように思えるのかもしれませんが、 機能呼び出しがメッセージになると、
  • ソースコードツアーや問題発生時のバックトレースが困難になる
  • コールグラフが作れないので、静的解析が困難
  • コンテクストスイッチが入ることで、処理負荷が増える
ということで、実行効率や解析性を落とすことになります。

このようなモジュールに紐付いた不必要なタスク分割は、先に書いたような過度なモジュール分割がされたシステムでよく見かけます。おそらく、モジュール分割した単位で担当者がかわっており、それぞれがつくりやすいように個々でタスク設計をしたために、不必要なタスク間イベントリレーができてしまったのではないかと思います。タスク設計については、「分けることはいいことだ」では決してありません。システム全体としての最適化という観点を意識しての設計が必要です。

過ぎたるは猶及ばざるが如し

モジュール設計にせよ、タスク設計にせよ、ただ一つの正解というものはありません。どちらにも言えることは、「何をつくるか」というだけでなく、「誰がつくるか」、「拡張性のスコープはどこまでか」を考えないといけないということ、そして、それを踏まえて、過度な分割をしすぎていないか確認すること、が大切かと思っています。

何事も、「過ぎたるは猶及ばざるが如し」ですね。
4434159372
【関連記事】
【関連書籍】

コメント

非公開コメント

本のおすすめ

4873115655

4274065979

4822236862

4274068579

4822255131

B00SIM19YS


プロフィール

  • Author:proger
  • 組み込み関係で仕事してます

ブログ内検索