1. プログラマーの仕事

多くの場合、初心者プログラマはプログラマの仕事について、経験豊富なプログラマとはまったく異なる考え方をしています。

初心者はよく「プログラムは動作しますが、他に何が必要ですか?」というようなことを言います。経験豊富なプログラマは、「正しく動作する」ことがプログラムの要件の 1 つにすぎず、最も重要なことではないことを知っています。

コードの可読性

最も重要なことは、プログラム コードが他のプログラマにとって理解できることです。これは、プログラムが正しく動作することよりも重要です。もっともっと。

正しく動作しないプログラムがある場合は、修正することができます。しかし、プログラムのコードが理解できない場合は、何もできません。

メモ帳などのコンパイル済みプログラムを取得し、その背景色を赤に変更するだけです。動作するプログラムはありますが、理解できるソース コードがありません。そのようなプログラムに変更を加えるのは不可能です。

教科書的な例としては、Microsoft 開発者がピンボール ゲームを 64 ビット アーキテクチャに移植できなかったため、Windows から削除したことが挙げられます。そして、そのソースコードも持っていました。彼らは単にコードがどのように機能するかを理解できませんでした

あらゆるユースケースを考慮

プログラムの 2 番目に重要な要件は、あらゆるシナリオを考慮することです。多くの場合、物事は見た目よりも少し複雑です。

初心者プログラマーは SMS メッセージの送信をどのように見ているか:

正しく動作するプログラム

プロのプログラマーは次のように考えています。

正しく動作するプログラム

「正しく動作する」シナリオは通常、考えられる 1 つのみです。そして、それが、多くの初心者が CodeGym のタスクバリデーターについて不満を言う理由です。10 シナリオのうち 1 つだけが機能し、初心者プログラマーはそれで十分だと考えています。


2. 異常事態

異常事態

どのプログラムの実行でも異常な状況が発生する可能性があります。

たとえば、ファイルを保存することにしましたが、ディスク領域がありません。または、プログラムがメモリにデータを書き込もうとしていますが、使用可能なメモリが不足しています。または、インターネットから画像をダウンロードしましたが、ダウンロード処理中に接続が切断されました。

それぞれの異常な状況について、プログラマー (プログラムの作成者) は、a) 異常な状況を予測し、b)プログラムがどのように正確に対処すべきかを決定し、c)望ましい状況にできる限り近い解決策を作成する必要があります。

そのため、プログラムは長い間非常に単純な動作をしていました。プログラム内でエラーが発生すると、プログラムは終了します。そしてそれは非常に良いアプローチでした。

ドキュメントをディスクに保存したいとします。保存処理中に、十分なディスク領域がないことが判明したとします。どの動作が最も望ましいですか:

  • プログラムが終了します
  • プログラムは実行を続けますが、ファイルは保存されません。

初心者プログラマは、プログラムがまだ実行されているため、2 番目のオプションの方が良いと考えるかもしれません。しかし実際はそうではありません。

Word で 3 時間文書を入力していましたが、作成プロセスを開始して 2 分後に、プログラムが文書をディスクに保存できないことが判明したと想像してください。2分の仕事を失うのと3時間の仕事を失うのはどちらが良いでしょうか?

プログラムが必要なことを実行できない場合は、すべてが正常であるかのように振る舞うよりも、プログラムを閉じた方がよいでしょう。プログラム自身で修正できない障害が発生した場合にプログラムができる最善の方法は、その問題をただちにユーザーに報告することです。


3. 例外に関する背景

異常事態に直面するのはプログラムだけではありません。これらはプログラム内、つまりメソッド内でも発生します。例えば:

  • メソッドはファイルをディスクに書き込もうとしていますが、スペースがありません。
  • メソッドは変数に対して関数を呼び出そうとしていますが、変数は null です。
  • 0 による除算はメソッド内で発生します。

この場合、呼び出されたメソッドでどのような種類の問題が発生したかがわかれば、呼び出し側メソッドは状況を修正できる (代替シナリオを実行する) 可能性があります。

ファイルをディスクに保存しようとしていて、そのようなファイルがすでに存在する場合は、ファイルを上書きするかどうかをユーザーに確認するだけで済みます。使用可能なディスク容量がない場合は、ユーザーにメッセージを表示し、別のディスクを選択するように求めることができます。しかし、プログラムがメモリ不足になるとクラッシュします。

かつて、プログラマはこの問題を熟考し、次の解決策を考え出しました。すべてのメソッド/関数は、実行結果を示すエラー コードを返さなければなりません。関数が完全に動作した場合、0が返されました。そうでない場合は、エラー コード(ゼロではない) が返されました。

このエラーに対するアプローチでは、ほぼすべての関数呼び出しの後で、プログラマーは関数がエラーで終了したかどうかを確認するチェックを追加する必要がありました。コードのサイズが大きくなり、次のようになりました。

エラー処理のないコード エラー処理を含むコード
File file = new File("ca:\\note.txt");
file.writeLine("Text");
file.close();
File file = new File("ca:\\note.txt");
int status = file.writeLine("Text");
if (status == 1)
{
   ...
}
else if (status == 2)
{
   ...
}
status = file.close();
if (status == 3)
{
   ...
}

さらに、エラーが発生したことを発見した関数は、それをどう処理すればよいのか分からないことがよくありました。呼び出し元はエラーを返さなければならず、呼び出し元の呼び出し元は呼び出し元にエラーを返す、という具合でした。

大規模なプログラムでは、数十の関数呼び出しの連鎖が一般的です。場合によっては、数百の関数の呼び出しの深さが見つかることもあります。次に、エラー コードを一番下から一番上に渡す必要があります。また、途中で何らかの関数が終了コードを処理しない場合、エラーは失われます。

このアプローチのもう 1 つの欠点は、関数がエラー コードを返した場合、関数自身の作業の結果を返せなくなることです。計算の結果は、参照パラメータを介して渡す必要がありました。これにより、コードはさらに複雑になり、エラーの数もさらに増加し​​ました。