ソフトウェアの構造を知ろう

再利用できる部分とできない部分を知ろう

 ソフトはだいたいの場合、何らかの目的を持っています。その目的を達成するために、計算をし、結果を画面に表示したり、ファイルに書きだしたりします。

 結果を画面に表示したり、ファイルに書きだしたりする操作は、戻り値を持ちません。ソフトの実行結果は、戻り値を持たないので、基本的に再利用できません。全く同じ目的を持つソフトなら、再利用することで目的を達成できますが、目的が少しでも異なる場合、そのソフトを直接再利用することはできません。

 再利用する場合は、「画面に書き出すためのデータを計算する処理」や、「ファイルに書き出すためのデータを計算する処理」をライブラリ化し、それを使って計算結果を戻り値として取得して、それを加工することで目的とする処理を実行することができます。

 つまりソフトは、「再利用できるライブラリ層」と「ライブラリを呼び出す再利用できない層」に分けることができます。たとえば、最上位にあるmain関数は再利用できません。main関数から呼び出されるライブラリは再利用できます。

再利用のレベルを分類しよう

 ソフトは、再利用できない上位層から、再利用できるライブラリを呼び出すことで目的とする処理を実行します。ライブラリには様々なものがあります。

  • ソフトの一箇所から利用されている処理
  • ソフトの複数ヶ所から利用されている処理
  • 別のソフトウェアからも利用されている処理

 再利用のされ方によって、処理をどのくらい汎用的に書けばいいかが決まります。

 再利用されない処理は、全く汎用性をもたせる必要はなく、目的のために必要な最低限の処理だけを書けば十分です。目的のために必要な処理だけをpublicにし、必要のないものはすべてprivateにします。

 逆に、別のソフトからも利用される処理は、今必要がなくても、必要になりそうな処理はすべて書いてpublicにしておく必要があります。

 その中間にあるのが、一箇所から利用されている処理、複数ヶ所から利用されている処理です。これらは別のソフトからは使われないので、今のソフトの目的に必要なものだけを書けばいいように思うかもしれません。

 しかしソフトの目的は移り変わりますし、新機能を搭載したくなることもあります。そういった仕様変更に対応できるようにするには、できるだけ処理を汎用的に書いておく必要があります。

 ただし、「汎用的にする必要が出てきてから汎用的にしたって手間は変わらないじゃないか。いつ起きるかわからない仕様変更のために先んじて汎用的にしておくなんて馬鹿げている」という考え方もあります。私も基本的にはその考え方です。

 しかし「その処理の一般形はなんだろうか」と考えてみることは重要です。その処理を適切に汎用化したら、たとえば、二次方程式のために場当たり的に因数分解の公式を当てはめてみるのではなく、解の公式を使うことはできないか。これだけあれば全部含んでしまう一般的な処理があるのではないか。と考えてみることです。

 一般形を見出すことが出来れば、仕様変更があっても変更する必要があなくなりますし、場当たり的な複数の処理が一個の一般的な処理に置き換えられれば、記述自体もシンプルにできることが多いです。

 しかし、一般化しすぎたために処理が抽象化されすぎて、何をやっているのかわからなくなってしまうこともあります。そういった場合はあえて一般化をせずに、直感的にわかりやすい個別の処理を書いて使った方が良い結果になるでしょう。