すべての開発において、「修正」という作業はかならず起こります。 修正にはいくつかの階層的レベルがあって、
等があります。
駄目なプロジェクトほど、この修正作業というものに多くの時間を取られています。 設計の悪さ(駄目なプロジェクトマネージャとSEによる)もありますが、 修正しにくい造り(駄目なSEとプログラマ)になっていると、 修正作業が長引きます。
修正作業を短くするには、列挙した、それぞれの修正状況において、 最も書き換える部分が少ない造りにしなければなりません。 書き換える部分が少ない造りというのは、 「同じ処理は同じ場所に1箇所に記述」 するのが一番効果的です。 これをできるかできないかが、できるプログラマとそうでないプログラマです。
できるプログラマというのは、複数個所に同じ記述が助長にあると、 それだけで直感的に不快を感じます。 修正作業の煩雑さを嫌というほど知っていて、また、 「プログラムはかなり高い確率で将来修正されるもの」という認識を持っているからです。 そして修正を行いやすいベストと思われる手段を用いてプログラミングをしていくことにより、 修正時間の大幅な削減を実現します。
それに対してダメなプログラマは、直感的に複数個所に同じ記述をべた書きしていきます。 「プログラムは今作っているものができればそれでいい」という認識だからです。 べた書きしたほうが確かにプログラムを組んでいるときは楽なのですが、 将来の修正を考えたコーディングは必要ですし、開発中にも修正作業というのは必ず発生します。 その時になって「あーめんどくせぇ」と言いながらたくさんの時間を修正に掛けるのがダメなプログラマです。
本ページでは、修正耐性への意識と技術について、 ダメなプログラマからできるプログラマへと変わるためのアドバイスを示します。 実際に言葉で説明するには限界があるので、不本意ながらプログラムを示しながら解説します。 言語は、最もスタンダードなC++言語を使用します。 本当に基本的な文法しか使わないので、C言語をちょこっと書いてみたことがある程度のレベルで読めると思います (読めなくても雰囲気をつかめれば、よいソースを書くための心構えがわかると思います)。
本ページでのこれからの説明には、ここで示すサンプルプログラムを使用します。
#include <stdio.h>
#include <string.h>
// comment out to alternatively border style
//# define BAR_ALT
# ifdef BAR_ALT
# define showBar(void) showAltBar()
# else
# define showBar(void) showSolidBar()
# endif
// comment out to change return string to <br/>
//# define RETURN_BR
# ifdef RETURN_BR
# define newLine(void) br()
# else
# define newLine(void) ln()
# endif
const short shMonitorWidth = 15; // 画面の横幅
const char chBar = '-'; // バーの文字
FILE* OUT = stdout; // 出力先
void br() {
fprintf(OUT, "<br/>\n");
}
void ln() {
fputc('\n', OUT);
}
void showAltBar() {
int i;
for (i=0 ; i<shMonitorWidth ; i++) {
if (i % 2 == 0) {
fputc(chBar, OUT);
} else {
fputc(' ', OUT);
}
}
newLine();
}
void showSolidBar() {
int i;
for (i=0 ; i<shMonitorWidth ; i++) {
fputc(chBar, OUT);
}
newLine();
}
void showText(char* text) {
int nIdxText; // 文字の位置
int nTextLength; // 文字の長さ
nTextLength = strlen(text);
// 文字を出力
for (nIdxText=0 ; nIdxText<nTextLength ; nIdxText++) {
fputc(text[nIdxText], stdout);
if (nIdxText % shMonitorWidth == shMonitorWidth - 1) {
newLine();
}
}
newLine();
}
int main(int argc, char* argv[]) {
char* str;
// 表示する文字列
if (argc >= 2) {
str = argv[1];
} else {
return -1;
}
// バーを引く
showBar();
// 文字を出力
showText(str);
// バーを引く
showBar();
return 0;
}
簡単に上記プログラムの動作を解説しますと、幅が15の画面に、 まず横棒を引いてから、文字を出力し、最後にもう一度横棒を引きます。
C:\>sample "I am the bone of my sword!" --------------- I am the bone o f my sword! ---------------
細かい設定とは、例えば使用する文字コード名、表示する文字、決められた値などの、定数のことをいいます。 サンプルプログラムでの定数はshMonitorWidthとchBarがこれにあたります。 それにconst修飾はされていませんが、ファイルポインタのOUTも細かい設定と呼べるでしょう。
もしも開発の最中に、画面の幅が15ではなく12しかないことが判明したとしましょう。 そうしたら、shMonitorWidthを15から12にすれば対応することができます。
もしも上下に表示するバーをアスタリスクで表示したいと顧客に言われたとしましょう。 そうしたら、chBarを'*'に変更すれば対応することができます。
もしも出力先をファイルにしたくなったら、OUTにはfopen関数等で返った値を入れておけば、 その後の出力はすべてファイルにされるようになります。
C:\>sample "I am the bone of my sword!" ************ I am the bon e of my swor d! ************
プログラミングをする際、定数にできるものは、すべて定数として宣言してしまうに越したことはありません。 一時的なテストプログラムを書いてみる等の場面ではそのようなことは意識しなくてもいいのですが、 簡単なプログラムであっても、定数化できるものは定数化する という心構えが大切です。
ある処理を実現したい場合に、それを実現するまでのプロセスは、いろいろな方法が考えられます。 ある程度作ってしまった、あるいは完成してしまったプログラムのある一部のアルゴリズムを書き換えようと思うたびに 該当するアルゴリズムのソースを書き換えていたのでは、その作業が発生する度に時間をとられてしまいます。
できるプログラマは、アルゴリズム等のまとまった処理は、きちんとモジュール化しています。 サンプルプログラムでは、ln関数、showBarマクロとそこから呼び出されるshowAltBar関数とshowSolidBar関数、 showText関数がモジュールとして用意されています。 これによって、修正したい箇所が発生したときに、プログラムの構造を大きく変えないで、 最小限のテストで修正できるようになります。
サンプルプログラムのでは、showSolidBar関数が使われていて、showAltBar関数は使われていません。 しかし表示するバーは、「-----」ではなく「- - -」というように、1つおきに表示したいと思い立ちます。 サンプルプログラムの場合、マクロを利用して
//# BAR_ALT
の行をコメントアウトするだけで、「- - -」というバーのスタイルにいつでも切り替えることができます。
C:\>sample "I am the bone of my sword!" - - - - - - - - I am the bone o f my sword! - - - - - - - -
また、結果をHTML等で利用するときのことを考えて、改行文字を"<br/>"としたい場合が出てきそうです。 そのような状態に対処するためには、
//# RETURN_BR
の行をコメントアウトするだけで、改行が<br/>になります。
C:\>sample "I am the bone of my sword!" ---------------<br/> I am the bone o<br/> f my sword!<br/> ---------------<br/>
このように、 ある特定の機能ごとにモジュールごとに関数にまとめておけば、切り替えが楽 になります。 「なにをそんな面倒くさいことを」と思うかもしれませんが、 ころころと要求が変わったり、またそれを自分だけではなく他の利用者もプログラムの場合、 利用者に合わせて動作を切り替えることが可能になります。
もちろん、こんな面倒くさいことをどこでもかしこもしなければならないわけではありません。 修正、変更、カスタマイズ等の可能性が高い部分のみでいいのです。 それだけで、開発中の仕様変更、開発後の保守作業、利用者の使い勝手が飛躍的に高まったものが出来上がります。
ちょっとしたデコレーション文字を変えてみたいとか、改行文字を変えてみたいとか、 そういった些細な部分の変更ではなく、プログラムの動作自体を変更してしまいたいという場合にも、 修正耐性のあるソースは、楽に実現することができます。
サンプルプログラムをよく見てみて下さい。 main関数には
この4つしか処理がされていません。 4つの処理はモジュールとしてmain関数の外に存在し、main関数内は全くごちゃごちゃしていません。 すべての処理をmain関数の中に詰め込んでいたら、こうはいかなかったはずです。
このことは、動作自体の変更にも、大変有利に働きます。 もしも上側のバーは2本引きたいとしましょう。 そうした場合、上側のshowBar関数の下に、もう1つshowBar関数を書いてやるだけです。
// 上側のバーを引く showBar(); showBar();
もちろん、2回ループしてやることも可能ですが、main関数の中の処理は、 一見してそのアプリケーションの処理の全体が見えることが好ましいので、 直感的に分かりやすい、2回showBar関数を記述するという方法を採りました (もちろん、これが3回とか4回繰り返すようだと、ループを使います)。
C:\>sample "I am the bone of my sword!" --------------- --------------- I am the bone o f my sword! ---------------
このように、普段から意味のあるまとまった機能はモジュールとして個々の関数等のある一定のかたまりにしておくことで、 それを扱う側からの動作の変更が可能になります。 また、モジュールとして独立して存在しているので、他のプログラム等で使う場合にも、再利用が可能になります。
今回、C言語のサンプルを用いて説明したわけですが、 実は、今は私はJavaが大好きというか、まずはJavaを極めようと思っている立場で、 C言語は、ここ1年ほど触れてませんでした。
しかしC言語が最も普及しており、なおかつ情報もあるので、選択しました。 C言語について全然知らない人でも、ポインタ等の難しい概念は使用していないので、 すぐに調べられると思います。 だから必ずサンプルプログラムのコードを読んでくれというわけではありませんが、 自分の得意な言語でないからというだけの理由で、情報を読み飛ばすのはあまり良くないことです。 あまり使ったことがない言語であっても、 ある程度は意味を汲み取れるだけのセンス(推察力と情報収集能力)を養っておくことは、とてもいいことだと思います。