Joshua Kerievsky 氏講演会「リファクタリングの戦略と戦術」

概要

URL http://patterns-wg.fuka.info.waseda.ac.jp/JK2010.html
日時 2010/03/18 18:30 - 20:30
場所 国立情報学研究所学術総合センター) 12階 会議室
twitterハッシュタグ #PWG_JK
講演タイトル Refactoring Strategies & Tactics
講演者 Joshua Kerievsky (Industrial Logic, Inc. and Cutter Consortium)

「パターン指向リファクタリング入門」の著者。

パターン指向リファクタリング入門~ソフトウエア設計を改善する27の作法

パターン指向リファクタリング入門~ソフトウエア設計を改善する27の作法

内容

What is Refactoring?


リファクタリング」より引用し、ソフトウェアの振る舞いを変えずに内部構造を変えることで、ソフトウェアをより容易に理解しより低コストで修正できるようにすることと説明

「Refactoring とは revision(見直し)であり、resee(再び見る)ことです」

リファクタリングは大きなものから小さなものまであるが、常に小さなステップで行う

ソフトウェア開発ではリリース(に向けて開発する)かリファクタリングの2つがある

A4の紙がえんえんと繋げられた動画


「これは28ページにわたるC++のコードです。それも1つのメソッドです」

これには私も含めて聴衆大ウケ。
1ページ40行としても 28 * 40 = 1120行。フォントが小さければもっと多い

このコードは10年間の Technical Debt(技術的負債)によって作られていった

「10年間ハミガキしなかったらどうなると思います? このコードは10年間掃除されてないんです」

5.x Software Development Lifecycle(バージョン5系ソフトウェア開発ライフサイクル)


ちゃんとメンテしないと大体バージョン5でにっちもさっちもいかなくなるよという経験則。

バージョン 状況
1.x very happy! みんなここで働くの楽しい! 全ては問題ない
2.x still pretty good まあまあ問題なし。まだあわてるような時間じゃない
3.x we are in trouble! あまりよくない感じ。変更にかなりの苦痛を伴う
4.x no one like to work this チームメンバがごっそり逃げていくレベル
5.x we need help! そうだアウトソースすりゃいいんじゃね?

「私は何年間も同じようなプロジェクトを見てきた」


M.ファウラーの「リファクタリング」は低レベルのリファクタリング
自分の著書は高レベルのリファクタリング

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

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

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

(まさかこのレポートに興味のあるような人で誤解するような人はいないと思いますが、低レベルと高レベルの話を批判や中傷の類などと思わないでくださいね)

デモ(0) 悪いリファクタリング


ここからは実際に Eclipse を使ってデモが行われた。
このパートは下記(1)の中で行われたものだが、話の流れ上敢えて順番を入れ替えた。

最初のデモは、いきなり一括置換とかであちこちリファクタリング

当然 JUnit はエラー出まくりで真っ赤。

「unsafe refactoring」

デモ(1) Parallel Change(架換工事)*1


SFのベイエリアの橋を紹介。新しい橋を完成させてから古い橋をなくしていく。

フィールドのリファクタリングに関する手順は以下の通り。

  1. フィールドのカプセル化でsetterを作る(Eclipse だと encapsulate field という機能で一括変換できるようだ。便利だな)
  2. 新しいフィールドを作り、古いフィールドの setter と並列に setter を配置。この状態は「ただ使われてないフィールドが存在してるだけ」なので当然 JUnit はエラーを出すわけがない。
  3. またフィールドのカプセル化を行い、getterを作る
  4. 古いgetterを新しいフィールド用のgetterで置換する
  5. テストコード内で古いフィールドを使っている箇所を書き換える(テストコード変えていいの?という質問があった。後述)
  6. 古いgetterを消す
  7. 古いsetterを空メソッドにし、Eclipseのインライン化という機能で一発消去。


よっぽど間違えない自信がない限り、一つ一つ修正してはテストを繰り返した方がいい(後述の Gradual Cutover)

以下の例は私(shiumachi)が講演内容を元にsetterを並列するところまでを PHP で書いたもの。

<?php

class Employee{
  var value1;

  public function __construct(){
    $this->value1 = 10;
  }
  
  public function method1($num){
    $this->value1 = $num;
  }

  public function method2(){
    return $this->value1;
  }

}

この value1 を newValue に変えたい場合はまず以下のようにする。

<?php

class Employee{
  var value1;
  var newValue;

  public function __construct(){
    $this->setValue1(10);
    $this->setNewValue(10);
  }
  
  public function method1($num){
    $this->setValue1($num);
    $this->setNewValue($num);
  }

  public function method2(){
    return $this->value1;
  }

  public function setValue1($num){
    $this->value1 = $num;
  }

  public function setNewValue($num){
    $this->newValue = $num;
  }

}

当然この状態ではロジックそのものに何も影響を与えない。(もちろんテストを流して確認する必要はある)

Refactoring Rash(リファクタリング性発疹)


不用意にリファクタリングをしてしまうと Eclipse が自動警告で赤マークをあちこちに出すのが発疹っぽい

Rash が出たら、腕をかきはじめたのが面白かった。いつかやってみたい

Graceful Retreat(優雅なる撤退)

A good retreat is better than a bad stand.
「上手な撤退は下手な抵抗よりもいい」
(アイルランドのことわざ。訳は shiumachi)

ようは「リファクタリングにしくじったらさっさと元に戻しましょう」ということだと思う。

Gradual Cutover(段階的カットオーバー)


これは Parallel Change でやったこと。

Preparing for Change(変更への準備)


子供いる人何人いますか?という質問

「じゃあ赤ちゃんが奥さんのお腹の中にいるときに、あなたはどのような準備をしましたか?」

Joshua 氏は、「子供のためのスペースを用意しました」

コードも同じで、変更する前にスペースを用意しておくこと。

(このあたりの話はあまり詳しくはされなかった)

デモ(2)Narrowed Change(範囲を限定した変更)


やってることは Parallel Change とそんなに変わらない。新しいフィールドを並列させることはないが、基本はテストをグリーンに保ったままリファクタリングしていく。

  1. setterの導入
  2. getterの導入
  3. setter,getterの中身をリファクタリングする

「Narrowed Change の方をよく使います。これがダメだったときに Parallel Change を行います」

デモ(3) Unified Methods(メソッドの統合)


2つの似たようなクラスがあって、同じ親クラスを継承していたら、名前の統一を行ったあとに Eclipse の pull up 機能で親クラスにフィールドやメソッドを渡してしまう。実装が異なる場合は抽象メソッドとして親クラスに定義しておく。

質疑応答


Q.テストコードを修正するのは危険では?
A.ロジックの大きな変更をしているわけではない。メソッド名の変更は問題ないと考える。これもできないと、リネームすらできないことになってしまう。しかし、その感覚は正しい。

Q.フレームワークへの依存性を弱めるには?
A.自分も数年前にXMLで同じような目にあった。XML2.0になったときに大変だった。このときはXMLBuilderというテンプレート用ツールを使って解決した。オープンソースのツールに依存し過ぎるのは危険なので注意してください。
(変化が速いので、オープンソースソフトのバージョンが上がるたびに大きな修正を強いられる危険がある、ということだと思う)

その他気になった発言
  • (リファクタリングのデモをしながら)almost like playing chess(ほとんどチェスやってるようなもの)
  • 手動テストは20世紀のものです
  • リファクタリングの前に充分なテストカバレッジが必要です
  • CV Driven Development(履歴書駆動開発)には注意すること。例えばJavaプログラマJavaプロジェクト経験も豊富な会社で、Rubyの経験豊富なエンジニアが「自分はrubyできるからrubyでやる」などというのはよくない。社内にはきっとJava用のライブラリやツールが豊富なはず。
    • (ちょっと保守的な考え方なので、個人的にはあまり極端過ぎるのもどうかと思う)
  • テストの自動化はイノベーションを加速する
  • リファクタリングはいいテストにつながる

感想

  • 著書「パターン指向リファクタリング入門」は読むしかないと思った
  • Eclipse をフルに使いこなしてるのを見ると、IDEもちょっといいかもと思った。Emacsであんな機能(リファクタリング機能とか)があればなー。
  • 「レガシーコード改善ガイド」にも「テスト駆動開発入門」にもあったけど、一歩一歩テストをグリーンに保ちながら進めることが大事だということだ。(事前にテストをきちんと整備しておくことはもちろんであるが)
  • リファクタリングがチェスに似てるというのはすごく同意。私は結構リファクタリングは楽しい、というのもリファクタリングはパズル的な面白さがあるからだ。複雑に絡み合ったコードを一歩一歩「警報システム」のトラップに引っかからないよう慎重に解きほぐしていくというのはやり始めてみるとかなりはまる。
  • 「レガシーコード改善ガイド」で学んだこととの共通点が多かった。基本はとにかく「scaffolding」。まずテストを用意。そのテストをグリーンに保ちながら慎重にリファクタリング。テストすらできない場合は「レガシーコード改善ガイド」に書かれた技術を使う他、今回学んだ Parallel Change も使えそうだ。足場を固めておけば強敵相手でも戦えそうだ。

*1:この訳語は私が勝手につけたもので、日本語圏におけるコンセンサスがとれたわけじゃありません。この架換工事という訳は、日本の建設業界における橋の架け換え工事の用語として定着していたことから使わせていただきました。最初「架換」としていたのですが、「架換工事」の方が語呂がいいので変えてみました。参考:[http://www.pref.chiba.lg.jp/kendo/kaiso/tyousi/osirase/choushioohasi.html:title]