BOOK Preview

Code Complete 第2版 上・下
― 完全なプログラミングを目指して

第34章 ソフトウェア職人気質とは

マイクロソフトプレスの書籍紹介ページ
書籍情報のページ
2005/05/24

Page1 Page2 Page3 Page4

 本コーナーは、.NET関連の新刊書籍から主要なチャプターをそのまま転載し、その内容を紹介するものです。

 今回は、日経BPソフトプレス/マイクロソフトプレスより2005年3月28日に発行の書籍『Code Complete 第2版 下 ― 完全なプログラミングを目指して』より、同社の許可を得てその内容を転載しています。

 同書は、11年前に出版された名著「Code Complete」の第2版です。第2版では、全体をとおしてオブジェクト指向の考え方が反映され、リファクタリングの章なども追加されています。また、開発言語としてC#やVisual Basic .NETも取り上げられています。“完全な”コーディングのための鉄則を凝縮した本書は、開発者ならば必読といえるでしょう。

 本記事では「第34章 ソフトウェア職人気質とは」を転載しています。実質的な最終章となる本章は、同書のまとめともいえる章です。著者によると、プログラミングは芸術にも科学にも分類されない「職人技」です。同書はソフトウェアの複雑さを低減し、コードの読みやさを実現するのに役立つ具体的なテクニックの集大成ですが、本章ではそれらをまとめながら、よい「ソフトウェア職人」となるための心構えを説いています。

 なお、書籍の詳細については書籍情報のページをご覧ください。

ご注意:本記事は、書籍の内容を改訂することなく、そのまま転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。

 本書のほとんどは、高品質なクラス、変数名、ループ、ソースコードのレイアウト、システムの統合など、ソフトウェアのコンストラクションの詳細に割かれている。本書では、より具体的な話題を際立たせるために、抽象的な話題をそれほど強調してこなかった。

 本書のここまでの部分では、具体的な話題を取り上げてきた。抽象的な概念への理解を深めたい場合は、さまざまな章から話題を拾い上げ、それらの関係を見てみるとよい。本章では、複雑さ、抽象性、プロセス、読みやすさ、反復といった抽象的なテーマに踏み込む。これらのテーマは、ハッキングとソフトウェア職人気質との違いの大部分を占める。

34.1 複雑さの克服

 上巻の「第5章 コンストラクションにおける設計」で、複雑さへの対処を「ソフトウェアの鉄則」として説明したように、複雑さを克服するための原動力こそ、ソフトウェア開発の真髄である。コンピュータサイエンスの問題を全階級で制覇するヒーローになりたい気持ちはわかるが、9桁もの詳細をカバーできる頭脳の持ち主は存在しない。コンピュータサイエンスとソフトウェアエンジニアリングは、そうした複雑さに対処するための知的な道具を次々と編み出してきた。そして、本書の他のテーマでも、それらの一部に触れている。

参照
複雑さを克服するという姿勢の重要性については、第33章の「33.2 知性と謙虚さ」を参照。

  • システムをアーキテクチャレベルでサブシステムに分割し、一部分ずつに集中できるようにする。

  • クラスのインターフェイスを入念に定義して、クラスの内部のしくみを無視できるようにする。

  • クラスのインターフェイスが表す抽象性を維持し、任意の詳細を覚えておかなくても済むようにする。

  • グローバルデータは使用しない。グローバルデータは頭の中で一度に考えなければならないコードの割合を大幅に増加させる。

  • 深い継承階層は脳に負担をかけるので避ける。

  • 深くネストしたループや条件文は使用しない。それらは頭脳労働の少ない単純な制御構造に置き換えることができる。

  • goto文は使用しない。goto文を使用するとコードがストレートではなくなるため、ほとんどの人が追いかけるのに苦労する。

  • さまざまなエラー処理テクニックを自分勝手に増殖させるのではなく、エラー処理の方法を慎重に定義する。

  • 組み込みの例外メカニズムの使用を体系化する。それらを規律を設けずに使用すると、goto文と同じくらい理解しにくくストレートでない制御構造になる可能性がある。

  • プログラム全体を覆い尽くすような巨大なクラスを作成しない。

  • ルーチンは短く保つ。

  • 「iは口座のインデックス、jは顧客のインデックス、それとも逆だっただろうか」といった細かな点を覚えておかなくてもよいように、説明の必要のない明確な変数名を使用する。

  • ルーチンに渡す引数の数はできるだけ少なくする。それにも増して重要なのは、ルーチンのインターフェイスの抽象性を維持するのに必要な引数だけを渡すことである。

  • 規約を設けて、コードの部分ごとに予想がつかない独断的な違いを覚えるという意味のない頭脳労働を避ける。

  • 一般に、上巻第5章で「偶発的な問題」として説明している問題をできるだけ克服する。

 複雑な評価はブール関数に組み込み、評価の目的を抽象化すると、コードの複雑さが和らぐ。複雑なロジックの連鎖をテーブル参照に置き換えた場合も、同様にコードの複雑さが和らぐ。明確に定義された一貫性のあるクラスインターフェイスを作成すると、クラスの実装の詳細を意識する必要がなくなり、作業全体が楽になる。

 コーディング規約を定める目的も、主に複雑さを緩和することである。フォーマット、ループ、変数名、モデリングの表記などに関する決断を標準化できれば、知的なリソースをプログラミングのより挑戦的な部分に回すことができる。コーディング規約が物議をかもす理由の1つは、美観的なベースがいくらか制限されるが、本質的には自由であることだ。人々はほんのわずかの違いをめぐって激しく対立する。規約が最も効果的なのは、任意の決断を下したりそれを弁護したりする苦労をなくす場合である。意味のある部分に制限を課すような規約には、それほど価値はない。

 抽象化は、さまざまな形で複雑さに対処する、特に強力なツールである。プログラミングは、プログラムの要素の抽象性を強化することで、大きく前進してきた。Fred Brooksは、コンピュータサイエンスにおける最も目覚しい進歩とは、マシン語から高級言語への飛躍であると論じている。これにより、プログラマは個々のハードウェアの不可解な動作に惑わされずに、プログラミングに専念できるようになった(Brooks 1995)。ルーチンという概念も大きな一歩であり、それがクラスやパッケージへとつながった。

 変数に名前を付ける場合は、実装レベルの解決策の「方法」ではなく、問題が「何」であるかに基づいて機能的な名前を付けると、抽象性がレベルアップする。たとえば、「スタックをポップする。それは最近入社した社員の取り出しを意味する」という場合は、これを抽象化して「スタックをポップする」という部分を省略し、「最近入社した社員を取り出す」という部分だけを考えればよい。これはわずかな節約だが、1〜109という複雑さの範囲を少しでも狭めたいなら、1つずつの積み重ねが大事である。リテラルの代わりに名前付き定数を使用することも、抽象性をレベルアップする。オブジェクト指向のプログラミングは、アルゴリズムとデータに同時に適用できるレベルの抽象性を実現するが、これは機能の分解だけでは達成できない抽象化である。

 要するに、ソフトウェアの設計とコンストラクションの主な目標は、複雑さに打ち勝つことである。多くのプログラミングプラクティスの背景には、プログラムの複雑さを低減するという動機がある。そして、複雑さを低減することは、ほぼ間違いなく、有能なプログラマであるための最も重要な鍵である。

34.2 プロセスの選択

 本書の第二の論旨は、ソフトウェア開発に使用するプロセスが驚くほど重要であることだ。小規模なプロジェクトでは、個々のプログラマの手腕がソフトウェアの品質を最も左右する。個々のプログラマを成功に導く要素の1つは、そのプログラマがどのプロセスを選択するかである。

 複数のプログラマが関与するプロジェクトでは、個人の能力よりも、組織としての体質が大きな違いを生む。どんなにすばらしいチームでも、その全体的な能力が単にチームメンバ個人の能力を合計したものであるわけがない。どのようなチームワークをとるかによって、個人の能力が相乗効果を生むこともあれば、逆に個々の能力を発揮できない状況を生むこともある。チームが使用するプロセスによって、1人の仕事がチームの仕事を後押しするのか、妨げるのかが決まる。

 プロセスが重要であることを示す1つの例は、設計やコーディングを始める前に要求を確定しなかった場合の結果である。何を構築しているのかがわからなければ、すばらしい設計にしようとしてもうまくいくわけがない。ソフトウェアが開発されている最中に要求が変更になり、それに伴って設計が変更されたら、コードも変更せざるを得ないので、システムの品質を低下させるおそれがある。

参照
要求を確定させることの詳細については、上巻第3章の「3.4 準備:要求」を参照。開発手法の種類については、「3.2 ソフトウェアの種類の特定」を参照。

 読者であるあなたは「もちろんそうだ。でも現実には、要求が安定することなどあり得ないから、それはごまかしだ」と言うだろう。この場合もやはり、どのプロセスを使用するかによって、要求がどれだけ安定しているか、そしてどれだけ安定している必要があるかが決まる。要求に柔軟性を持たせたい場合は、インクリメンタルな開発方針でのぞみ、ソフトウェアをすべて一度に完成させるのではなく、段階的に構築する計画を立てればよい。これはプロセスを意識したものであり、そのプロセスはプロジェクトの成功の行方を左右する。上巻第3章の表3-1は、要求のエラーがコンストラクションのエラーよりもはるかに高くつくことを明確に示している。したがって、プロセスのその部分に重点を置くことは、コストとスケジュールにも影響する。

 プロセスを意識するという原則は、設計にも当てはまる。設計に基づいてコンストラクションを開始するためには、しっかりとした土台を築かなければならない。土台が完成していないのにコーディングを急ぐと、システムのアーキテクチャに基本的な変更を加えることは難しくなる。既にコードを書いていたら、設計に愛着が湧いているだろう。家を建ててしまってからでは、土台が悪くても捨てるのは難しい。

真剣なプログラマへ。日々の作業時間の一部を独自の手法の調査と改良に充てよう。プログラマはいつも将来や過去の締め切りと格闘しているが、方法論的な抽象化は、賢明なる長期的な投資である。
─ Robert W. Floyd

 プロセスが重要である主な理由は、ソフトウェアでは最初の段階から品質の改善に努めなければならないということである。これは、死に物狂いでコーディングし、後からソフトウェアの誤りをすべてテストすればよいという、民衆の知恵と矛盾する。この考え方は完全に間違っている。テストはソフトウェアにどのような欠陥があるかを示すものにすぎない。プログラムをより便利なものにしたり、速くしたり、小さくしたり、読みやすくしたり、拡張性を高めたりするわけではない。

 早まった最適化も、プロセスエラーの1つである。効果的なプロセスでは、最初に大雑把な調整を行って、最後に細かな調整を行う。彫刻家は、特徴を1つずつ仕上げていく前に、だいたいの全体像を整える。早まった最適化は時間を無駄にする。なぜなら、磨き上げる必要のないコードを磨き上げることに時間をかけるからである。もう十分に小さいコードや十分に速いコードに手を加えたり、結局捨ててしまうことになるコードを磨き上げたり、既に時間をかけてしまったという理由でできの悪いコードを捨てられなかったりする。「正しい順序で行っているか。順序を変えると違いが出るか」を常に検討しよう。意識的に良いプロセスに従おう。

 下位レベルのプロセスも重要である。擬似コードを書いてから擬似コードの周りにコードを埋めていくというプロセスに従うと、トップダウン方式の設計の利点が得られる。また、コードにコメントが付いていることが保証されるため、後からコメントを付ける手間が省ける。

参照
反復の詳細については、「34.8 反復、その繰り返し」を参照。

 大きなプロセスと小さなプロセスを観察することは、ソフトウェアを作成する方法に注意を払うために立ち止まることを意味する。ここに時間をかけるのは良いことだ。「コードこそが重要なのであり、抽象的なプロセスではなく、コードの品質に主眼を置かなければならない」と言うのは、先見の明のない考えであり、それを反証する経験上および実践上の証拠の山を無視している。ソフトウェア開発は創造的な活動である。創造的なプロセスを理解していないとしたら、ソフトウェアを作成するのに必要な第一のツールである脳を十分に活用していないことになる。悪いプロセスは頭脳労働を無駄にする。良いプロセスは頭脳労働を最大限に活かす。

 

 INDEX
  Code Complete 第2版 上・下
  第34章 ソフトウェア職人気質とは
  1.34.1 複雑さの克服
    2.34.3 人間が1番、コンピュータは2番
    3.34.6 問題領域に立ったプログラム
    4.34.8 反復、その繰り返し
 
インデックス・ページヘ  「BOOK Preview」

@IT Special

- PR -

TechTargetジャパン

Insider.NET フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

イベントカレンダー

PickUpイベント

- PR -

アクセスランキング

もっと見る

ホワイトペーパーTechTargetジャパン

注目のテーマ

Insider.NET 記事ランキング

本日 月間
ソリューションFLASH