開発現場の"Clean Code"

エンタープライズ・アプリケーションのプログラミング現場における"Clean Code"について、本の紹介を交えながら考えていきます。

導入

いわゆる「1人月」のタスクを「とりあえずこなせる」PGを目指す場合であっても、覚えるべき知識はそれなりに広範囲に渡っていると思います。最低限のシンタックスに始まり、データ構造としてのListとMap、制御のためのif文/for文、要するにロジックを書くための「データ構造とアルゴリズム」、ユーザインターフェイスの作成言語、永続データ操作のためのSQL、開発のためのIDE、その他お便利ツール、何よりもプロジェクトで使用されているフレームワークの使い方、コーディング規約など各種の規約、テストケースのあげ方、書き方などなど。とはいえ、これらは範囲こそ広いものの、一つ一つに関して言えば実はそう難しいものではなく、がっちりとした標準モデルが構築されているプロジェクトであれば、こういった一通りのことを覚えることでとりあえず動くものは作れるようになります。しかし、この「とりあえず動くものが作れる」という状況が実は危険で、とりあえず仕事がこなせてしまうだけに、そこで止まってしまう人が少なからず出てきてしまう場所なのではないかと思うのです。

読みやすいコード

コードを書く上でとにかく大事なのが「コードは人が読むもの」という概念です*1。ここから「仕様通り動けば良い」ということではなくて、「読みやすいコードを書く必要がある」ということにつながっていくのですが、しかしそもそも「読みやすいコード」とはどういうコードなのでしょうか。これについては「デザインとして優れている」ということとは少し違った観点から考える必要があると思います。


「デザインとして優れている」ということについて、少し補足しておきます。「オブジェクト指向」に正確に準拠したコードの場合、それを理解することは「クラス間の関係を理解すること」になっていきます。したがってこのレベルにおいては、読みやすいコードにとって重要なのは、1つには「クラスが現している概念が抽象レベルで明確であること」ですし、同時に「デザインパターンへの準拠」でもあると言えます。しかし、このレベルのデザインは難易度がそれなりに高く、また標準的なエンタープライズ・アプリケーションの個別開発者という役割を担う方にとっては試みる機会自体、あまり無いかとも思います。というのも、フレームワークないし標準モデルが確立されている開発プロセスにおいて、個別開発者にとってのコーディングとはほぼ、特定のイベント(代表的なものが「ボタン押下」)に対応するメソッドの中身を実装することになるからです。Struts系でいう、Actionクラスの1メソッドと言えば分かりやすいでしょうか。このようなデザインが規定された状況下での「読みやすいコード」がこのエントリのテーマです。

処理の流れ

Uncle Bobなどの本を読むと、「1つのメソッドは1つのことしかやってはいけない」と書かれています。これは究極的には正しいことだと思うのですが、クラス構成に制約がある状態で内部メソッドを細分化しすぎると、それはそれで処理全体の流れが良く分からなくなってしまうような気がします。


「処理全体の流れ」という観点から考えると、Kent Beck氏が"Implementation Patterns"において行っている指摘が重要だと思われます。

As an author/programmer, you decide whether to express the flow you have in mind as one main flow with exception, multiple alternative flows each of which is equally important, or some combination. You group bits of the control flow so they can be understood abstractly at first, for the casual reader, with greater detail available for those who need to understand them.


著者/プログラマとしてあなたは、思い描いている処理の流れを表現するのに、例外を伴う1つのメインフローとするのか、それぞれが同じく重要な複数のフローとするのか、あるいはそれらの組み合わせかということを決定しなさい。細かいコントロールフローをグループにまとめることで、さらっと読む人がまず抽象的に理解できるようにしなさい(強調引用者)。その上で詳細を必要とする人にもそれが分かるようにしなさい。
Beck(2007), p.64

さらに、メインフローに関しては次のように書かれています。

Therefore, clearly express the main flow of your program. Use exceptions and guard clauses to express unusual or error conditions.


したがって、プログラムのメインフローを明確に表現して下さい。通常ではない状態やエラーの状態を表現するにはExceptionとガード節を使用して下さい。
Beck(2007), p.64

ここでのポイントは「さらっと読む人がまず抽象的に処理の流れを理解できるようにすること」です。この「抽象的」という考え方はUncle Bobの"Clean Code"でも取り上げられており、「関数は1つのことしかやってはいけない」ということに関連して次のように説明されます。

One Level of Abstraction per Function
In order to make sure our functions are doing "one thing," we need to make sure that the statements within our function are all at the same level of abstraction.


1つの関数につき、1つのレベルの抽象度
関数が「1つのこと」しかやっていないことを確実にするためには、関数に含まれるステートメントが全て同じレベルの抽象度にあることを確実にする必要があります。
Martin(2008), p.36

この抽象度という考え方についてもう少し掘り下げてみます。

「意味のレベル」と「実装のレベル」

「抽象」という概念はソフトウェア開発において極めて重要な(もしかしたら最も重要な)概念であると思います。しかし、どう抽象度のレイヤを分割するのかということについては画一的に決めることができず、コンテキストの中でしか決定できません。かといって、「抽象度のレイヤリングが人によって異なる状態」はかなり悲惨で、それならばいっそのこと全部1つのメソッドに書いてくれていた方がまだ理解できるということになりかねません。だから「プロジェクト内でコードの統一感を考える上では、抽象度の適切なレイヤリングを行い、それを共有しなさい」ということにはなるのですが、ここではもう少し詳しく、一般的に適用できると思われる抽象度の2つのレベルについて考えたいと思います。


この2つのレベルが見出しに挙げた「意味のレベル」と「実装のレベル」です。言い換えるならば「業務のレベル/技術のレベル」、もっといえば「What/How」と言えるでしょう。一例として、特定の条件でデータベース検索を行う場合をとりあげます。

  • 意味のレベル:画面の入力情報を検索条件として○○一覧を取得する。
  • 実装のレベル:△△クラスのインスタンスを生成して、××を引数に□□メソッドをコールする。

要するに「意味のレベルで実装されたメソッド」とは「お客さんが読んでも分かる設計書」の抽象度でコントロールフローが書かれているものということになります。

抽象レベル分割のためのリファクタリング

実装のレベルから意味のレベルを明確に分離しておくためのテクニックを、Fowler氏の古典『リファクタリング』から紹介しておきます。この本自体は「読みやすいコード」を逆の視点からとらえていて、問題のあるコードの兆候を「不吉な匂い」と表現し、それを修正していくためにはどうすれば良いのかが語られています。デザインに関するアドバイスも少なくありませんが、ここでは「第6章 メソッドの構成」で説明されているワザを2つあげます。

  • メソッドの抽出:ひとまとめにできるコードがある場合に、それをメソッドにして目的を表すような名前をつける。
  • 説明用変数の導入:式の結果を、目的を表す名前をつけた変数に導入する。


要するに実践自体はそれほど高度な技術が要求されるものではなく、意識しているかしていないかだけの話だと思います。ただし1点注意があるとすれば、意味のレベルで命名されるプライベートメソッドおよび説明用変数の命名方法は、統一的に決められている必要があるということでしょうか。例えば、「画面」を表現するのに開発者によって、"display / screen / gamen"などとばらついているのは好ましいものではありません。非アルファベット言語の宿命として、共通チームが少しがんばる必要がある部分だと思います。

最後に

実際の開発現場の観点から「読みやすいコード」について整理しましたが、結局一番重要なのは開発者の意識なのだと思います。最後に、文脈は異なるもの、核心を的確に突いている素晴らしいアドバイスを引用しておきます*2

The next time your program begins to work, think of how you might make it better. Ask yourself this: If Ernest Hemingway, James Michener, Neil Simon, Frank Lloyd Write, and Pablo Picasso could not get it right the first time, what makes you think you will?


次にプログラムが動き始めたら、どうすればより良いものにできるかを考えて下さい。自分にこう問いかけて下さい。「アーネスト・ヘミングウェイも、ジェイムズ・ミッチェナーも、ニール・サイモンも、フランク・ロイド・ライトも、パブロ・ピカソも、常に最初から正しいものを作る事はできなかった。それなのに、なんで自分にはできると思ってしまうのだろうか?」
Heckel(1991), p.117

参考文献

Implementation Patterns (Addison-Wesley Signature Series (Beck))

Implementation Patterns (Addison-Wesley Signature Series (Beck))

Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin Series)

Clean Code: A Handbook of Agile Software Craftsmanship (Robert C. Martin Series)

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

  • 作者: マーチンファウラー,Martin Fowler,児玉公信,平澤章,友野晶夫,梅沢真史
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/05
  • メディア: 単行本
  • 購入: 94人 クリック: 3,091回
  • この商品を含むブログ (312件) を見る
The Elements of Friendly Software Design

The Elements of Friendly Software Design

プログラマー現役続行 (技評SE新書)

プログラマー現役続行 (技評SE新書)

*1:プログラマー現役続行』の中でも現役続行に必要な7つの力の1つに「読みやすいコードを書く力」が挙げられています。

*2:UIデザインについて解説しているこの本において、この箇所が示唆しているのはプログラム自体の改善であって、中身であるコードのリバイスではありません。しかし「ものを作る」ということ全てに通じる真理だと思います。