InfoQ

News

テスト駆動開発とレガシーコードのトラブル

作者 Mark Levison , 翻訳者 吉田 英人 投稿日 2009年11月23日 午後3時8分

コミュニティ
Architecture,
Agile
トピック
アジャイル技術,
プログラミング
タグ
テスト,
TDD,
Legacy Code

原文(投稿日:2009/11/19)へのリンク

Complex MazeAlan Beljeu 氏は古い (レガシー) C++ コードベースで TDD を行っていて,トラブルに見舞われた。その理由はこうだ。

機能を完全に実装できていないクラスが最後に残ります。いつか必要になるかも知れない,というやつです。他のクラスからそれを利用しようとして,実装を完成させる時がきた,まさにその時になって当初の設計不足が明らかになるのです。設計は新たにやり直し,外部仕様(とそのテスト)も修正が必要。そのクラスを使っていた既存コードも変更しなければなりません。

そして彼は,"事前の大規模設計 (Big Design Up Front)" がこの問題の解決策ではないか,と考えるのだが,アジャイルコーチである George Dinwiddie 氏は,Alan のこの例が訴えているものを指摘する。すなわち,きれいなコード (clean code) の原理に注意を払わなくてはならない,基本的な結合度 (coupling) と凝集度 (cohesion) が必要である (つまり SOLID),ということだ。

アジャイルコーチ である Mike “Geepaw” Hill 氏は,自身の長年にわたるアジャイルチームのコーチ経験から,次の中のどれかが問題の根源にある,と言っている。

  • リファクタリング作業への習熟不足のため,クラスが最小限になっていない
  • 簡略化に関するスキル不足のため,クラスが最小限になっていない
  • マイクロテスト (別名ユニットテスト) が積極的かつ迅速に行われていないため,コード変更によるテスト失敗の頻度が高すぎる
  • チーム間の依存性,公開された依存性の扱い方が分かっていない 例)API 出荷
  • おそらくは,揺れのないビルド (jiggle-less build) が存在しない
  • 1940年台のツールを使用しているのかも知れない

XP コーチKeith Ray 氏は,レガシーコードのように大きな技術的負債を持つシステムを扱う場合には,その返済コストがストーリの実装コストの大部分を占める,と指摘した上で,次のようなアプローチを提案する。

コードを構造的に改善する (技術的負債の支払いを行う) ためには,新機能の実装が必要なときには常に,新しいコードと古いコード両方の状態に十分な注意を払わなければなりません。その結果としてどちらかのコードにまずい部分が見つかったならば,リファクタの実施を検討するべきです。

リファクタは手作業で,安全な大きさを単位として行うのがよいでしょう (たとえ C++ であっても)。リファクタリングに関する Fowler 氏の著書にある説明を読んで,それが暗記できるまで注意深く従いましょう。Eclipse 上で gcc を使用する場合には,抽出 (Extract) や名前変更 (Rename) などいくつかのリファクタリング機能が利用できます。名前変更はスコープを認識するので,検索・置換よりも安全です。ただし抽出メソッドと,Eclipse のこれ以外のリファクタリングはバグがあります。使用する場合には注意してください。また,関数のシグネチャを変更するような場合には,変更が必要な部分を"コンパイラ頼み"で見つければよいでしょう。

リファクタリングが既存コードを壊していないことを確認するためには,テストを行う必要があります。Feathers 氏の著書である “working with legacy code” には,レガシコードにテストを追加するためのテクニックがたくさん紹介されています。もっと上位の作業レベルから見て,まずいコード (code smells) というのは,よい設計指針に従っていないものです。例えば単一責任原則 (Single Responsibility Principle,SRP)によると,すべてのクラス/メソッド/モジュールの目的はひとつでなければなりません。他にも凝集度,結合度,依存管理などに関する原則があります。一方でこのような抽象的な原則を適用するよりも,まずいコードを探す方が簡単な場合もよくあります。"巨大なクラス","巨大なメソッド" は "クラス抽出" や "メソッド抽出/移動" で治療できます。それでも SRP を知っていた方が,クラスやメソッドのどの部分を抽出すべきかを決定する上で役には立ちます。

最も重要な設計原則はおそらく "命じよ,求めるな (Tell, don't ask)" でしょう。機能とデータを合わせて保持せよ,という意味です。悪いコードでは機能と,それに必要なデータが別々のことがよくありますが,これはローカル性の欠如と依存性の問題を引き起こします。その象徴が "機能追加時に多くのコード変更が必要になる" という現象でしょう。まずいコードのパターンである "ショットガン手術 (shotgun surgery)","機能の嫉妬 (Feature Envy)","長いパラメータリスト" などがこれにあたります。

迅速なフィードバックの獲得がさらなるリファクタリングを可能にして,(ついには)新機能のより早い開発を可能にします。並列ビルド (分散コンパイル) が実行できるようにしましょう。より小さなソースファイル,ヘッダファイルを作りましょう。ヘッダファイルの複雑性を下げるために,前方参照定義の利用,インラインコードの回避を心がけましょう。ヘッダファイル/ソースファイルにはひとつのクラスを記述するようにしましょう。"pimpl" イディオムを広く採用するとコンパイル時間を10%程度減少できますが,"大きなクラス" や "機能の嫉妬 (Feature Envy)" が隠れている場合もあります。

書き直しを行う場合と比較すると,リファクタリングの利点は "動作するコードが常に手元にある" ということです。手動ないし自動で行うテストの内容が十分なものであれば,たとえ状態が悪い設計からよい設計への中間段階にあったとしても,コードの出荷は可能です。

Keith は “Refactoring: Small Steps Guaranteed to Help You Clean Up Your Code” という,C++ コードをリファクタする記事を Better Software Magazine に寄稿してもいる。

これまでに InfoQ で取り上げた関連記事は次のものだ。"Dealing with Legacy Code","Bobおじさんが述べるTDDの適用可能性","TDDを根づかせる:導入の問題と解決策"

ブックマーク
digg+,
reddit+,
del.icio.us+,
dzone+,
slashdot+
Hatena

特集コンテンツ一覧

Agile Japan 2009

2009年4月22日、東京千代田区にある放送会館で「アジャイルジャパン2009」が開催されました。本イベントは「ソフトウェア開発の次世代リーダーをつくる」ことを合い言葉に、200人以上の参加者を集めてスタートしました。

Flex 4の新機能トップ10

今週(2009年6月1日)AdobeはFlex 4の正式な初ベータ版をリリースしました。Flex 4はGumboというコードネームで開発されています。今回のリリースには大きな変更が多数含まれています。このRIAフレームワークの最新バージョンにおいて変更された事柄についての概要を以下のリストで見ていきましょう。

Domain Driven Design(ドメイン駆動設計) Quickly 日本語版

ビジネス領域の深い理解を反映したドメインモデルを設計するための、ヴィジョンとアプローチです。この本は、Eric Evans氏の「Domain Driven Design」の主要点を短く読みやすく要約しました。

JavaプログラマがFlexとBlazeDSを学んだ方がいい13の理由

この記事ではJavaプログラマがなぜFlexとBlazeDSを学ぶべきなのかについて13の理由を述べています。なぜ高度にインタラクティブなWeb サイトからJavaで開発されたバックエンドをもつエンタープライズ・アプリケーションまでを含む、リッチ・インターネット・アプリケーション(RIA)の開発にFlexとBlazeDSの組み合わせが最適な選択肢となるのかについて述べています。

仮想パネル: バックログは重要な成果物とプラクティスか、それとも無駄か?

Mary Poppendieck氏、Ron Jeffries氏、Jeff Patton氏、David West氏、Steve Freeman氏、Jason Yip氏が、バックログに関する彼らの意見とアジャイルチームを成功させるために必要な事を語った。

Perf4Jを使ったパフォーマンス解析とモニタリング

この記事ではAlex Devine氏が、Java開発者がPerf4Jをどのように利用できるかと、タイミングステートメントにコードを追加し、ロギング、結果の解析とモニタリングを行うオープンソースツールセットの説明をします。

複雑な外部DSLを開発する

本稿では、Vaughn Vernon氏が内部DSLと外部DSLの違いを説明し、複雑な外部DSLを開発する際のステップを示します。

J2EEアプリケーションにおけるAOPを使ったフェッチ戦略の実装

この記事では低レベルのサービス・レイヤやリポジトリ・レイヤを肥大化させることなく、フェッチング・ストラテジによってモジュール化された方法でバックエンドにあるシステムからデータを取得する処理を最適化する方法について説明します。