「岸辺露伴 新宿へ行く」展を見にグッチ新宿に行ってきた
10/6、つまり今週木曜までということで、見れなくなる前に行ってきた。
店舗の前には常に10人ほどが携帯で撮影をしている感じで、私もとりあえず撮ってみた。
3Fでは、実際の原稿などが見れる。
最後には等身大の三次元の露伴が立っている。ここで履いているスニーカーが売れてるらしい。
3Fは撮影禁止と言われたので、写真は外のものだけだが、全体を通して迫力があって良いなぁと思った。
15分もあれば全部見れるだろうと思うので、あと2日、行ける人は是非どうぞ。
久しぶりに商品をぐるっと見たけど、GGを重ねてハート型にしたアイデアはうまいと思った。
4年くらい前に出たウェブのリボン、その後のハートに続いて、近頃のかわいらしいデザインは日本の女性に似合うと思う。
「リファクタリング」の完全読破。その7、7章前半
7章は2回に分けて。今回は前半部分について。
第7章 オブジェクト間での特性の移動
オブジェクトの設計において根幹をなすのは責任をどこに配置するか。
しかし、ファウラーでも「責任を初めから正しいところに配置することができません。」と言う。
後からでも、リファクタリングを使って適切に配置していけば良い、ということだ。
それに、システムは変化していくということを忘れてはいけない。
P146 ある週では正しく適正であった設計判断も、次の週にはそうでなくなります。それが問題なのではなく、それについてなにもしないことが問題なのです。
このような状況を受け入れなければならない。
受け入れた上で、下記のような方法で解消していく。
メソッドの移動(P142)
メソッドが、定義しているクラスよりも他のオブジェクトを参照することが多い場合に検討する。
移動することでクラスが単純になり、責任の集合をすっきりした実装に収めることができる。
ただし、サブクラスまたはスーパークラスで利用しているメソッドであり、ポリモーフィズムをまるごと表現できないならば移動してはいけない。
メソッドの移動を行うメソッドへの参照が多い場合は、すべて書き換えるのは困難なので委譲メソッドを残す。
(コード例はフィールドの移動と合わせて後述。)
フィールドの移動(P146)
フィールドが、現在または将来に渡って、定義しているクラスよりも他のクラスから使われることが多いときに行う。
はじめに「フィールドのカプセル化(P206)」、「自己カプセル化フィールド(P171)」を行う。つまりprivateにしてアクセサを作る。
その後、フィールドの移動と参照しているメソッドの修正を行う。
上記二つの例を合わせて示す。
まずは、リファクタリング前のコード。
class Account{ private AccountType _type = new AccountType(); private int _daysOverdrawn = _type.daysOverdrawn; //メソッドの移動前 double overdraftCharge(){ if(_type.isPremium()){ double result = 10; if(_daysOverdrawn > 7) result += (_daysOverdrawn - 7) * 0.85; return result; } else return _daysOverdrawn * 1.75; } //フィールドの移動前 private double _interestRate; double interestForAmount_days(double amount, int days){ return _interestRate * amount * days / 365; } }
class AccountType{ public int daysOverdrawn; public AccountType() { daysOverdrawn = 8; //本来はもっと複雑な式 } boolean isPremium(){ return true; //本来はもっと複雑な式 } }
上記のコードをリファクタリングすると、下記のようになる。
なお、このコード例はあくまで手法を示すものであり、リファクタリングを適用する状況の判断は自分自身で行う。
(実際に、ここに書いたコードだけを見ると、私はメリットがあるとは思えない。)
class Account{ private AccountType _type = new AccountType(); private int _daysOverdrawn; //メソッドの移動後に委譲にする場合。そうでなければ呼び出し元をすべて修正する。 double overdraftCharge(){ return _type.overdraftCharge(_daysOverdrawn); } //フィールドの移動後、アクセサに変更する。 double interestForAmount_days(double amount, int days){ return _type.getInterestRate() * amount * days / 365; //利用するメソッドが多い場合は_type.getInterestRate()を返すgetInterestRate()を作るのも良い。 } }
class AccountType{ public int daysOverdrawn; public AccountType() { daysOverdrawn = 8; } boolean isPremium(){ return true; } //メソッドの移動後 double overdraftCharge(int daysOverdrawn){ if(isPremium()){ double result = 10; if(daysOverdrawn > 7) result += (daysOverdrawn - 7) * 0.85; return result; } else return daysOverdrawn * 1.75; } //フィールドの移動後、アクセサも定義する。 private double _interestRate; double getInterestRate() { return _interestRate; } void setInterestRate(double arg){ _interestRate = arg; } }
クラスの抽出(P149)
クラスは、きっちり抽象化されたものであり、少数の明確な責任を担うべきである。
しかし、実際にはクラスは成長していくので、大きすぎて理解出来なくなったクラスは切り離す必要がある。
切り離し、元のクラスから新クラスにリンクを貼り(必要があるまで双方向にはしない)、低レベルなメソッドから移動していく。
(切り離したNewClassに対して、元のOriginalClassからリンクを貼る) class OriginalClass... private NewClass _newClass = new NewClass();
クラスの抽出後に考えるのはアクセス制御。
publicは問題が多く、複製してから外部に渡すことも混乱の元になる。
ここでは元クラスを利用することを強制するか、新クラスを不変にするか、新クラスの不変インタフェースを用意する方法が薦められている。
また、最後に「クラスの抽出は、並列プログラムの並列実行可能性を向上するための一般的な技法」とも書かれている。
クラスのインライン化(P154)
クラスの抽出の逆。特に注意は無し。
7章は次回に続く。
「リファクタリング」の完全読破。その6、第6章後半。
前回は「第6章 メソッドの構成」から『総論と「メソッドの抽出(P110) 」「説明用変数の導入(P124)」』について書いたので、今回は残りの項目を。
大きなものは「メソッドオブジェクトによるメソッドの置き換え」くらいで、あとは項目名のとおりという印象。
(※下記のコード片は、本書の例を元に短縮等を行ったもの。)
第6章 メソッドの構成
「メソッドのインライン化(P117)」
不要な間接化、つまり委譲のみ行っているようにみえたら行う。単純な場合にのみ行い、複雑な場合は避ける。
return (moreThanFive()) ? 10 : 0; boolean moreThanFive(){ return _preAmount > 5; } ↓ return (_preAmount >5) ? 10 : 0;
「一時変数のインライン化(P119)」「問い合わせによる一時変数の置き換え(P120)」
「一時変数のインライン化(P119)」は、よく「問い合わせによる一時変数の置き換え(P120)」の一部として使う。
先に変数をfinalにしてみるのがコツ。エラーがなければ一度しか代入されていないことがわかる。
これらも、一度だけ代入されるか副作用がない場合に行う。
複数回代入されるなら「一時変数の分離(P128) 」、副作用があるなら「問い合わせと更新の分離(P279)」を適用することを考える。
//一時変数のインライン化 double todayPrice = anOrder.getTodayPrice(); return (todayPrice > 1000); ↓ return (anOrder.getTodayPrice() > 1000); //問い合わせによる一時変数の置き換え double basePrice = _quantity * _itemPrice; return basePrice * 3; ↓ return basePrice() *3; private double basePrice() { return nowPrice * quantity; }
「一時変数の分離(P128)」
複数回代入される一時変数は、代入ごとに別の一時変数に分ける。
複数回設定されるのは、複数の責任を担っていることを示す。 複数の責任を担う変数は、コードを読む人を混乱させる。
double temp = 2 * (_height + _width); System.out.println(temp); temp = _height * _width; ↓ final double perimeter = 2 * (_height + _width); System.out.println(perimeter); final double area = _height * _width;
「パラメータへの代入の除去(P131)」
int discount(int inputVal) if(inputval > 50) inputVal -= 2; ↓ int discount(int inputVal) int result = inputval; if(inputval > 50) result -= 2; //代入が悪いだけで、メソッドの使用はなんら悪いことではない。 void aMehod(Object foo) foo.modifyInSomeWay(); //OK foo.setBar(); //OK foo = anotherObject; //NG
この項目で重要なのは、上記の例よりも ”Javaは、厳密にはすべて値渡しを使っている””本質的に、オブジェクトへの参照は値渡し”ということ。
「メソッドオブジェクトによるメソッドの置き換え(P135)」
メソッドの分解を困難にするのはローカル変数である。Method Objectパターンを使う。
ちょっとこれは大きいので、無意識に出来るようになるまで何度か本書を読み直した方が良さそう。
class Account int gamma(int a, int b, int c){ int value1 = (a * b ) + delta(); int value2 = (a * c) +100; if ( (c - value1) >100 ) value2 -=20; int value3 = value2 *7; return value3 - 2 * value1; } ↓ class Account int gamma(int a, int b, int c){ return new Gamma(this, a, b, c,).compute(); }
TDDの調子で、抽出したGammaクラスとcomputeメソッドを書く。
これでリファクタリングの対象はGamma.compute()になる。というテクニック。
「アルゴリズムの取り替え(P139)」
文字通り、よりよいアルゴリズムに変更する。項目として必要なのはわかるんだけど、内容はタイトル以上のものでもないので割愛。
以上で第6章は終了。
「リファクタリング」の完全読破。その5
今回からカタログに入る。第6章は内容が多いので2回にわけて。
今回は、総論と「メソッドの抽出(P110) 」「説明用変数の導入(P124)」をしっかりと。
第6章 メソッドの構成
総論
下記の通り重要な章。いくつかの項目を除けばクラス階層の変更が不要なので、やりやすいという面もある。
(経験上、クラスを作ったり消したりすると面倒になる職場が多い。悲しいことに。)
P109 メソッドを、適切にパッケージ化されたコードとして構成することが、本書のリファクタリングの大部分です。 問題を起こすのは、ほとんどの場合、長すぎるメソッドなのです。 メソッドを分解すると、その動作がより深く理解できます。そして、そのアルゴリズムがもっとわかりやすくできることに気づくかもしれません。
さて、いったいどこまでメソッドを小さくするといいのだろうか。
私はここに書いてある通りと考える。
P110 メソッド名とメソッド本体の間の意味的な距離が重要なのです。コードを抽出することで明快さが向上するなら、そうすればよいのです。 抽出されるコードよりも名前のほうが長いとしてもです。
ファウラーは、現実的、実務的な人だという印象が強い。(これは他の著書でも強く感じる。UMLでは顕著に。)
ここでも、見た目や体裁より如何に実際の作業で効果が上がるか、ということが書かれている。
本章の構成
本章では「メソッドの抽出(P110)」がメインとなり、それをいくつかのリファクタリングが支えている。
例えば、「問い合わせによる一時変数の置き換え(P120)」で一時変数を取り除く。
困難な場合は先に「一時変数の分離(P128)」を行う。
それでもダメなら「メソッドオブジェクトによるメソッドの置き換え(P135)」を適用する。
パラメータの場合は「パラメータの除去(P131)」を行う。
また「アルゴリズムの取替(P139)」を検討する。
などなど。(これらは次回取り上げる。)
知っての通り、今では簡単なリファクタリングは自分で書く必要はない。
例えば「メソッドの抽出」なら、Eclipse上で括って右クリックし「リファクタリング」「メソッドの抽出」を選択、そしてメソッド名を入力すると自動で変換される。
しかし、もちろんリファクタリングはそんな簡単なものばかりではない。
効果的なリファクタリングは、プログラマが意図的に書かなければならないものが多いので、しっかり学ばなければならない。
「メソッドの抽出(P110) 」と「説明用変数の導入(P124)」
「メソッドの抽出」は本章のメインとなるリファクタリングである。
「説明用変数の導入」は適用したくなる場面が多いが、ファウラーはその場合も「メソッドの抽出」を適用することを推奨している。
ここでは、両者を比較することで理解を深めようと思う。(本書でもそうしている。)
まずは修正前のコード。こういうコードをよく見る人も多いのでは。(下記のコード群は全てP125〜P127を元にしたもの。)
double price(){ //価格は、基本価格ー数量割引+送料 return _quantity * _itemprice - //基本価格 Math.max(0, _quantity - 500) * _itemPrice * 0.05 + //数量割引 Math.min(_quantity * _itemPrice * 0.1, 100.0); //送料 }
まず、_quantity * _itempriceが複数回書かれており、意味もコメントの通りなのでbasePriceとして抽出する。
そして、Math.maxの行と、Math.minの行もコメントの通りに「説明用変数」として抽出する。
それだけで「説明用変数の導入」は完了する。
ここで重要な点は「コメントを取り除ける」ということ。修正後のコードは、コード以上のことを何も表現していないからだ。
(とはいえ、それでも書けというプロジェクトはいくらでもありそう。ご愁傷さまです。)
double price(){ final double basePrice = _quantity * _itemPrice; final double quantityDiscount = Math.max(0, _quantity - 500) * _itemPrice * 0.05; final double shipping = Math.min(basePrice * 0.1, 100.0); return basePrice - quantityDiscount + shipping; }
十分わかりやすいが、ファウラーは上記のとおり「メソッドの抽出」を推奨している。
それを適用するとこうなる。
double price(){ return basePrice() - quantityDiscount() + shipping(); } private double basePrice() { return _quantity * _itemprice; } private double quantityDiscount() { return Math.max(0, _quantity - 500) * _itemPrice * 0.05; } private double shipping() { return Math.min(basePrice() * 0.1, 100.0); }
読みやすさとしては同じようなものだと思う。
メソッドにする利点は「他の部分からも使える」ことが大きく、必要に応じてprivateの解除を行って良い。
できる限り「メソッドの抽出」を行い、「説明用変数の導入」 を使うのは「メソッドの抽出」では手間がかかる場合だとファウラーは書いている。
今回書いたリファクタリングは、頭の硬い上司(やそれに準じる体制)がいてもできるのではないかと思う。
次回は、これを支えるリファクタリングについて記載する。
続く。
「リファクタリング」の完全読破。その4
今回は第4章と第5章。この次から、いよいよカタログに入る。
第4章 テストの構築
多分ここが、古いということで一番割を食ってる箇所。
JUnitの説明などは、今は他の本を読んだ良いと思う。
所々出てくる、今読んでも色褪せないなと思う部分だけ引用する。
(古い古いと書いているが、未だにJUnitすら使っていないシステム開発現場はあるし、むしろ経験上そっちの方が多い。)
P89 リファクタリングに限らず、よいテストを書くとプログラミングが加速するようです。 P90 テストを完全に自動化して、その結果もテストにチェックさせること。 P94 テストを頻繁に実行せよ。コンパイル時にはテストを局所化して、1日に最低1度はすべてのテストを実行せよ。 P97 バグレポートを受け取ったら、まずそのバグを明らかにするための単体テストを書け。 P98 不完全なテストでも、書いて実行するほうが、実行できない完全なテストよりもましだ。 P99 失敗の恐れのある境界条件を考えて、そこを集中的にテストせよ。 P100 失敗すると予測されるときに、例外が上がることをテストし忘れないこと。 P101 テストですべてのバグが見つからないからと入って、テストを書くのをやめてはならない。ほとんどのバグはテストで補足される。
テストのやめ時について、いくつか補足する。
カバレッジを100にする意味があるときと、あまり無いときがある。
まずはテストにおける収益低減点を考えること。
適当な時間で大部分のバグを補足するほうが良い。
継承とポリモーフィズムを踏まえるとテストの組み合わせはどんどん増えていくが、全部の組み合わせを試す必要はない。
ふと見ると、Kent Beckの「テスト駆動開発入門」も、相当に古い本だった。
私が持っているのは2008年の第3刷だけど、日本語の初版は2003年。
リファクタリングもTDDも、思想と技術は確実に身になってるんだけど、職場では強制されないし、逆に強制できないというのが少しもどかしい。
第5章 リファクタリング・カタログに向けて
こちらはタイトルのとおり、カタログ(第6章〜第12章)の記述方針等の説明。
注意点として、リファクタリングの説明に集中するために、金額をdoubleとしたり本来はまずしないような書き方もしているということ。
(それを踏まえると、Javaのバージョンが古いということも、ある程度は受け入れられるかな。)
また、GoFには「デザインパターンはリファクタリングの目標を提供する」と書かれており、リファクタリングを使うとき、デザインパターンはその出発点であることを心に留めるようにと書かれている。
・過去記事。
「リファクタリング」の完全読破。その1 - holyppの日記
「リファクタリング」の完全読破。その2 - holyppの日記
「リファクタリング」の完全読破。その3 - holyppの日記
「リファクタリング」の完全読破。その3
帰省などで間があきましたが、今回は第3章。8月中の読破を目標に。
第3章「コードの不吉な匂い」
テーマは、いつリファクタリングを始めていつ終えるか。
(P76) ここではリファクタリングの必要を示す不吉な兆候について説明していきます。 インスタンス変数はいくつ以上になれば多過ぎであり、メソッドは何行以上で長過ぎるかなどの感覚は、自ら養っていかねばなりません。
他に「経験で磨かれた人間の直感には、メトリックスをいくら集めてもかなわない」とも書かれており、経験の重要さが強調されている。
各項目はそれぞれ半ページから2ページで文量は少ないが、引用されている手法を確認しながら読むとかなり時間がかかる。
本エントリは後から見直せるように、見出しと簡単な説明を書いていく。
重複したコード
同一クラス内の複数メソッドに同じ式がある場合。
完全に同じでなく似通っている場合は、共通に使える部分とそうでない部分を分離し、TemplateMethodの形成(P345)を検討する。
複数のメソッドが、同じ処理を違うアルゴリズムで実装していた場合は、アルゴリズムの取り替え(P139)を適用する。
関係の無い二つのクラス間で、重複したコードがある場合は、クラスの抽出(P149)を行い処理を委譲する。
長すぎるメソッド
メソッドが長くて、ソースを追わないと何をしているかわからない場合。
メソッドの分割時に問題になるのは引数や一時変数が多いメソッドで、先に以下の手法を使ってスリム化を行う。
「問い合わせによる一時変数の置き換え(P120)」
「引数オブジェクトの導入(P295)」
「オブジェクトそのものの受け渡し(P288)」
「メソッドオブジェクトによるメソッドの置き換え(P135)」
条件分岐やループも「条件記述の分解(P238)」を使って抽出を検討する。
巨大なクラス
インスタンス変数を持ち過ぎたクラス、コード量の多いクラスは「重複したコード」の温床。
多すぎる引数
オブジェクトをそのまま渡し、メソッドがさまざまなデータをそこから取り出せば良い。
変更の発散、変更の分散、パラレル継承
設計は、一つの変更要求(DBの変更や商品追加など)に対して、1つのクラスの1箇所が常に修正対象となるようにする。
以下のような状態になったらリファクタリングを行うべき。
変更の発散:一つの変更に対して同一クラス内の複数のメソッドを少しずつ修正しなければならない。
変更の分散:一つの変更に対して複数のクラスを何度も修正しなければならない。
パラレル継承:一つの変更に対して複数の継承木にそれぞれサブクラスを作らなければならない。
属性、操作の横恋慕
見出しの翻訳が良いなと思う。
あるメソッドが他のオブジェクトのgetメソッドを何度も何度も呼び出している場合、メソッドの位置が悪い。
ただし、StrategyパターンとVisitorパターン(と、Self Delegationパターン)など例外はある。
上記のパターンは変更に必要な部分(この場合は振る舞い)を1か所にしているので、パターンを優先する。
データの群れ
「数個のデータがグループとなって」属性やシグニチャに頻繁に現れる場合は、オブジェクトとしてまとめてみる。
基本データ型への執着
基本データ型ではなくオブジェクトを使う。
例えば「電話番号」を最初に文字列で定義し、後から頻繁にフォーマット変更などの振る舞いが必要になった場合、オブジェクトにすると良い。
スイッチ文
スイッチ文を見たらポリモーフィズムを使って解決できないか考える。
1章で行った「State/Strategyによるタイプコードの置き換え(P227)」や、null値で分岐している場合の「ヌルオブジェクトの導入(P260)」など。
怠け者クラス
十分な仕事をしないクラスは排除する。「階層の平坦化(P344)」、「クラスのインライン化(P154)」など。
疑わしき一般化
「いつかこの機能が必要になるさ」と凝った仕掛けを作った場合。例えば、あるクラスやメソッドがテストケースでのみ利用されている場合など。
一時的属性
インスタンス変数の値が特定の状況でしか設定されない場合。クラスとして切り出すか、ヌルオブジェクトを使う。
メッセージの連鎖
クライアントがオブジェクトにメッセージを送り、そのオブジェクトが更に別のオブジェクトにメッセージを送り、更に……という場合。
getHoge()のような呼び出しが長い行となって連なっていたり、一時変数が非常にたくさん定義されているとあやしい。
仲介人
メソッドの大半が別のオブジェクトに委譲しているだけのクラスは除去する。
不適切な関係
これも翻訳が良い。見出しも本文↓も。
仲の良すぎるクラスは、いにしえの恋人たちのように、遠くに離してあげねばなりません。
この場合は「双方向関連の単方向への変更(P200)」、「以上による継承の置き換え(P352)」を検討する。
クラスのインタフェース不一致
処理が同じでシグニチャのみが異なるメソッドは「メソッド名の変更」を行う。
(見出しと説明文を読んでも、イマイチ何が言いたいのか理解できない。どういう場合?)
未熟なクラスライブラリ
再利用はオブジェクト指向の目的とよく言われるが、万能なクラスライブラリというのは非常に難しいもの。
既存のクラスライブラリに追加したいメソッドがある場合は「外部メソッドの導入(P162)」、「局所的拡張の導入(P164)」を行う。
データクラス
get,setメソッド以外に何も持たないクラスに、利用しているクラスから振る舞いを移せないか検討してみる。
接続拒否
サブクラスが親の属性と操作の一部しか利用していない場合、継承階層が間違っているというのが伝統的な見方になる。
兄弟となるクラスを新たに作成し、「メソッドの引き下げ(P328)」、「フィールドの引き下げ(P329)」を行う。
サブクラスが「振る舞いは継承するけどインタフェースは必要無い」場合は「委譲による継承の置き換え(P352)」を行う。
コメント
コメントは良いものだが、それが「わかりにくいコードを補うため」だとしたら修正する必要がある。「表明の導入(P267)」など。
はてなブックマークでRSSリーダーにいれるべき、たった一つのタグ
これ、入れてみてはどうか。
タグ「インタビュー」を検索 - はてなブックマーク
何年か、いろいろと見てるけど、私が安定して面白いと思うのはこれだけ。
実に3割くらいは面白い記事で、それはすごい高い確率だと思う。野球の打率と同じだね。
他に、Ruby、Vim、お役立ち、ガジェットなどをはじめ、いろいろと入れてるけど、それらの記事はやっぱりピンキリで、そこから自分のフィルターが必要になってしまう。
それらの本文は、総合すると1割も見ないので、かなり弾く必要がある。
インタビューというのはすなわち、あるテーマにそった成功者の言葉だ。
ほとんどは、私のような凡人が考えられないくらいストイックな思想をもとにしているし、そうでなくても、自分では考えつかないような面白い(新しい)視点のものが多い。
手塚治虫は
「一流のマンガ家になりたいのなら、一流のものを読んで、一流のものを観て、一流のものを聴きなさい。」
と言っていたが、それは真理だと思う。
「はてブはあんまりなぁ」という人も多いと思うけど、「インタビュー」タグは一ヶ月くらいチェックしても、悪いことは無いと思うよ。