新しい言語や使ったことのない機能などを学ぶ場合、雑誌やウェブにあるサンプルのソースコードを参考にする人は多いと思います。当然ですが、私もその一人です。数多く目にするソースコードですが、「アプリに利用する場合」は、そのまま使ったり、ほぼ真似て作るのはお薦めできません。それには大きな理由があるので説明します。
そもそも、雑誌やウェブ、またはメーカーが提供するソースコードの目的は何でしょう。たいていの場合は、作りたい機能を実現することです。つまり、機能の実現を重視したソースコードとして書かれています。
そうした目的のために一番適したコーディング方法は、具体的な命令文をベタで書くことです。説明のためのコメントは加えますが、それ以外では命令文だけをベタに記述します。どのような命令を組み合わせ、どの順序で並べれば確実に動くのか、もっとも理解しやすい方法だからです。
命令文をベタに羅列する書き方は、命令文以外の要素が含まれません。そのため、余計な説明が不要になります。再利用のために何々を工夫したとかいう話は、目的の機能を実現するためには関係ないのです。つまり、機能の実現方法を説明する目的には、命令文をベタに書く方法が最適なわけです。
では逆に、アプリを開発するときのソースコードは、どんな点を考えて書かれるのでしょうか。もちろん、人によって考えは違うでしょう。でも、アプリ内の構造を上手に作るとしたら、意見の違いは少ないのではないでしょうか。いろいろな点が考えられますが、実現するのが難しくて、もっとも重視したい点となると、変更への柔軟性です。将来の拡張を十分に考慮し、変更が発生したときの手間が最小限で済むように、いろいろと工夫して書くはずです。
次に挙げる点としては、コードの再利用があります。一度作った機能は、別なアプリで使うときに、手直しせずに使えたら非常に助かります。新しく書くソースコードの量が減るからです。無駄な開発作業を減らす、もっとも有効な手段でしょう。
つまり、ソースコードを作る目的というか、どんな点を重視してソースコードを書くのかが、雑誌のサンプルとアプリ開発では根本的に違っているということです。この点に気付かない人が、意外に多いかもしれません。
少し余談になりますが、柔軟性や再利用以外の考慮点として、実行速度が挙げられます。最近は、それほど気にしなくて良くなりましたが、完全になくなったわけではありません。ただし、以前とは状況が違ってます。
実行速度が問題になりそうなアプリを作るときでも、何から何まで速度重視で作るケースは減りました。ツールが発達したことで、ネックになっている処理部分が見付けやすくなったことも大きいです。速度重視の開発でも、手順としては次のように行なうケースが増えていと思います。最初は、スッキリした構造で作り、一応の機能を完成させます。もし遅くて困る機能が発見されたら、解析ツールを使って、遅い原因となっている処理を特定し、その部分に改良を加えます。こうした手順だと、全体として良い構造を確保しながら、速度問題も解決できる可能性が高まります。
もし最初から処理速度が問題になりそうなアプリでも、開発を始める前に大まかな解決方法を考え、その方法に沿いながら、アプリの構造を良くする方法まで検討します。良い構造を抜きにして開発するのは、もう受け付けられない状況になっていますね。
話を戻して、変更への柔軟性や再利用を重視した作り方を取り上げましょう。具体的には、どのようなソースコードになるのでしょうか。
まずは、よく使う機能のライブラリ化です。同じ命令コードを繰り返し書かなくて済むように、機能ごとにまとめて、異なるアプリでも共通して使えるように作ります。クラスとして作る場合も、関数群として作る場合もあるでしょう。どちらにするかは、実現する機能の特徴に加え、プログラマーの好みにも関係するでしょうね。いや、本当に好みなんですよ。
さらに考えるのは、機能の切り分けと、切り分けた部分のインターフェースです。たとえば、アプリで作成した内容を、プリンターで印刷したいとします。大きく分けて、印刷内容を生成する処理と、その内容をプリンターに渡して印刷する処理が考えられます。後者の処理は、使用するプリンターに依存し、プリンターごとに作る必要があります。
そうなると一番良い切り分け方は、次のようになります。印刷内容を生成する処理を独立させ、プリンターへ渡す部分のインターフェースを標準化することです。プリンターに渡して印刷する処理は、プリンターごとに作りますが、標準化されたインターフェースに準拠して作るわけです。
こうすると、次のようなメリットが生まれます。印刷内容を生成する処理では、プリンターを切り替えても、修正する箇所が最小限で済みます。同時に、プリンターを扱う処理も独立でき、別なアプリへ持っていきやすくなります。インターフェースを標準化していますから、持っていった別なアプリでも、複数のプリンターを切り替えたとき、呼び出す側の変更が最小限で済みます。
このように見ていくと、機能の切り分けが重要であると理解できます。それぞれの機能が交換しやすく、また他へ持っていきやすくなるように、上手に切り分けることです。さらに、切り分けた部分のインターフェースを標準化して、切り分け後の同種機能を交換可能に作る点も、非常に重要です。上手な切り分けと、切り分けたインターフェースの標準化が、ソフトウェア設計で非常に重要なことなのです。
現在はオブジェクト指向プログラミングが広く普及していますが、これらの切り分けは、オブジェクト指向には依存しません。古くから使われている手続き型のプログラミング言語でも、簡単に実現可能です。その意味で、プログラミング言語の種類に依存せず、もっとも基本的な設計方法と言えるでしょう。
ライブラリ化も含めて、機能を区切る際に一番重要なのが「どこで区切るか」です。上手に区切らないと、メリットを大きくできません。考え方としては、似た機能を交換できるか、そのまま別なアプリへ持っていけるか、という点を重視して決めます。場合によっては、区切りを2段階にして、交換できる可能性を増やすこともあります。何度も試して経験する中で、少しずつ上手になると思います。
続いて、広く役立つのが間接参照です。色などの値を定義して、それを参照する使い方が一般的でしょう。上手なプログラミング手法として、C言語などでも古くから使われています。
それ以上に役立つのが、関数などの処理を通した間接参照です。関数を間に入れることで、色々な機能を盛り込めます。たとえば、現在の時刻を得る関数を用意して、アプリに含まれる全部の時刻参照で、その関数を使っているとします。すると、その関数の処理を変更し、通常よりも時間が速く進む機能を加えたり、指定した時間分だけ時刻を早めたり遅らしたりも可能になります。さらには、特定の日付時刻で実行させるといったトリックも、簡単に実現できます。
アプリ上では、メッセージを表示するためのUI部品を付けるでしょう。その場合、UI部品を直接参照するのではなく、UI部品を参照するための関数を用意するのが、間接参照による作り方です。メッセージを表示する個々のソースコードでは、UI部品での代わりに関数を呼び出す形になります。すると、メッセージ表示に特別な機能を追加したいとき(表示時刻を追加するとか)、関数だけを修正して対応できます。
値定義でも関数でも、事前に間接参照を使っておけば、1カ所を変更するだけで、参照している全部の値や機能を変えられます。つまり、変更への柔軟性が高いプログラムになるわけです。
以上のような工夫を数多く用いると、どのようなソースコードになるでしょうか。よく使う機能はライブラリ化して、用意した関数やメソッドを呼び出す形になります。間接参照を多用すると、値定義や関数を呼び出す形が増えます。
結果としてソースコードは、関数やメソッドが中心となり、ベタで書いた命令文の率が極端に減ります。雑誌などの見かけるような、命令文をベタで書いたソースコートとは、まったく別物に見えるはずです。アプリ内部の構造を上手に作れる人ほど、その傾向が強いでしょう。
ここまでの話を理解すると、ソースコードの作り方が大きく変わると思います。雑誌などのソースコードを参照しても、そのままコピーして使わずに、個々の命令文をプログラム部品として用いながら、アプリ全体の構造に当てはめていくでしょう。まるでソースコードを再構成するような感じで。
よく使う機能をライブラリ化したり、同類の機能と置き換えやすいように処理内容を区分けしたり、そのインターフェースを標準化したり、いろいろな箇所で間接参照を組み込んだり、様々な工夫が思いつくはずです。こうした考え方で、アプリの内部を設計すれば、優れた構造で作れると思います。
プログラムの再利用の話になると、オブジェクト指向の話が必ず登場します。でも、オブジェクト指向のプログラムング言語を使い、それを生かしたブログラミング手法(デザインパターンなど)で作ったとしても、再利用しやすいソースコードになるとは限りません。
たとえば、小型プリンターRoltoのサンプルコードは、Objective-C言語を用い、オブジェクト指向の考え方で書かれています。しかし、プリンター処理がアプリ処理と一体化しているため、プリンター処理だけ独立して、他のアプリへコピーしては使えません。また、他のプリンターに切り替えたとき、アプリの処理もかなり変更しなければなりません。最近使ったという理由でRoltoを例に挙げましたが、他のプリンターでもほとんど同じです。
これらのソースコードは、前述したように、ドライバーの使い方を説明するのが目的です。そのため、再利用しやすいコードを目指してはいません。結果として、オブジェクト指向に則って作りながらも、再利用しづらいソースコードの例となってしまいました。
つまり、オブジェクト指向も道具の1つでしかなく、より上位のレベルでのソフトウェア設計において、再利用を考慮することが大事だということです。ここで書いたようなアプリ設計の考え方を採用することで、オブジェクト指向の利点が、さらに有効に活かせるようになります。
ここまでの話を、最後に整理しましょう。大事な順に並べると、次のようになります。
変更への柔軟性や、再利用を重視したソースコードを作る視点
1. 機能の切り分け(インターフェースの標準化も含む)
2. 間接参照を多用する
3. オブジェクト指向の利点を利用する(デザインパターンも含めて)
この中では、オブジェクト指向だけが特殊です。他の上位2つは、オブジェクト指向のブログラミング言語でなくても利用できる、ソフトウェア設計術に位置づけられます。
アプリを開発する際には、ここで取り上げたような視点で、アプリの内部構造を考えてみてはいかがでしょう。そうすれば、雑誌などに載っているサンプルのソースコードをそのまま使わず、細かな命令文を部品として再構成するようになり、変更への柔軟性が高くて再利用しやすいソースコードが得られるはずです。
0 件のコメント:
コメントを投稿