[ホームへ戻る] [前へ] [上へ] [次へ]

リファクタリング

プログラミングスタイル

プログラミングをするスタイルには以下の二つがあります。

1.行き当たりバッタリのプログラミング
  できるところから、思いつくところから、思いつくままに進めて試行錯誤しながらやっていき、共通化できるところをメソッド、クラスにしていく。

2.クラス・メソッドのの構造をあらかじめきちんと設計してから行う。

優秀なプログラマはすべて2のタイプで、1は初心者だと思っている人が結構います。しかし、実際には優秀なプログラマでも1のタイプの人は結構います。パッと思いついたことをダーッと書き、それを修正して、結果としてきちんとしたクラス構造ができているといった感じです。ある程度経験のあることなら、頭の中に設計図があるので、それに沿ってプログラムを書き、外部にクラス設計などを書くことはありません。

最初にきちんと設計をしたつもりでも、実装段階では想定したとおりに行かなかったり、抜けがあったりすることはよくあることです。案ずるよりは生むが易しで、実際に書いているうちにいろいろ思いつくこともあります。

とはいえ、このアプローチだと、ソースがつぎはぎだらけになる可能性もあります。ちょこっと例外的な処理を付け加えて、を繰り返し、コードは汚くなっていきます。そうしてソースがスパゲッティ化し、メンテ不能のソースとなっていきます。

そうならないようにするためにリファクタリングがあります。リファクタリングとは、仕様を変えることなく、ソースコードを改造することをいいます。汚いソースコードでもプログラムは動きますが、メンテナンスするのは大変です。汚いソースコードと綺麗なソースコードではメンテのしやすさが全く違います。なのできるだけ綺麗なソースを書くように心がけ、汚くなったら速やかに書き直すことです。後々のことを考えて、わかりやすいソースに直します。

ただ、その都度リファクタリングしていけばいいのですが、面倒くさいと思うと、そのままにしてしまったりします。また、ある程度出来上がったものに対する修正は、根本から直すことにもなり、その手間ひまを考えると難しいものです。



動いているソースには手を加えない?

一般に、現在動いているコードは、すでにテスト済みで、動作が保証されているため、「動いているソースには手を加えるな」と言われています。これはもっともなことです。多くの人が、動いているソースに安易に手を加えて痛い目にあった経験があることでしょう。「何でかよくわからないけど動いているからまあいいや」なんて言葉も聞かれ、まさに触らぬ神にたたりなしで、下手なことはしない方がいい場合があります。

ですので、改修や追加開発で、ソースコードをいじらなければならない場合は、修正箇所を限定して、その影響範囲を明確にします。修正箇所がわかるようにコメントをつけて、それ以外に直したい箇所があっても、正常に動作している限りはいじらないことです。特に既存のソースを別の人・別の会社が作成し、改修案件に自分が参加している場合、余計な責任を被らないためにも、手を触れないことです。私もしばしば改修案件で一から作り直したいと思うようなひどいソースに出合ったことがあり、既存の潜在バグも十分ありそう、というか、あるものでしたが、そこには手を触れず、修正要件に関わる部分だけをいじり、インデントや行末のスペースさえも変えないようにしたものです。

一般的にはこれが現実的な選択肢です。

テストの自動化

もしリファクタリングを行うのであれば、必須なのがテストの自動化です。これなくしては、リファクタリングは現実的ではありません。ソースコードを大幅に改造したとき、既存の機能が正しく動作するのかをいちいち手動で全部確認するのは、手間とコストがかかります。

テストを自動化しておくと、短時間で動作が確認できるので、大胆にソースを改造していくことも可能です。

ただしです。これは、そのテストが全項目を網羅している場合です。もし自動化しているテストに抜けがあるなら、そのテストをパスしたとしても、バグが潜んでいる可能性は十分にあります。修正前のテストの一部を手動で行っていたり、あるいはたまたま動いていたような場合、注意する必要があります。

実際他人の作ったテストケースや、あるいは自分で作った場合でも、記憶が薄れるような昔に作ったようなテストケースは信用なりません。完全に仕様を理解しているなら、テストケースが網羅できているかどうかチェックできますが、既存のプロジェクトでは、仕様のすべてはドキュメント化されておらず、あるいはドキュメントに嘘が含まれており、テストケースが完全であるかどうかをチェックするのは難しいものです。

なので、自動化されているからといって妄信しないことです。一方、自分で新規に開発を行っている場合は、テストケースすべてを網羅するようにして、どんどんリファクタリングを行っていくべきでしょう。

設計の重要性−−リファクタリングには時間がかかる

大規模なリファクタリングには時間がかかります。テストの自動化をしていても時間がかかります。アプリケーションのアーキテクチャを変更するようなことが後でないように、できる限り想定できる範囲まできちんと設計しておきます。特に業務アプリケーションのような、似たような処理をたくさん作り、しかもそれぞれ形式を統一しなければならないような場合、後でアーキテクチャーを変更すると修正はかなり大変になります。単純置換できる場合はいいですが、手作業となると膨大な時間がかかります。

テストの自動化をしなくても

リファクタリングについてバイブル的な存在は、マーチン・ファウラの「Refactoring: Improving the Design of Existing Code」(リファクタリング―プログラムの体質改善テクニック)という本です。

テストの自動化をしなくても、リファクタリング、というよりも、綺麗なソースコードの書き方として、この本に書いてあることは知識として知っておいて損はないでしょう。というよりも、プログラマであれば当然知っているべきことです。

なので、私は、この本はプログラマ必読の書と考えています。

この本は非常によくできた本で、多くのサイトで取り上げられています。私もこのページで、いろいろ解説しようかと思いましたが、すでに優れたサイトがあるので、このページでは、個人的な備忘録としてメモ書きを連ねていきます。

参考になるサイトは、Googleで「リファクタリング」「refactoring」で検索すればたくさん出てきます。特に気になったのは以下のサイトです。

Refactoring Home Page
  リファクタリングのホームページで、新たなリファクタリングカタログも追加されています。

リファクタリング勉強ノート
  最初のリファクタリングの例のソースコードがダウンロードできるようになっていて、解説もされています。

SmellsToRefactorings

Smells to Refactorings Quick Reference Guide

  不吉な匂いと対応が表にまとまっています。

また以下の本も、よくまとまっています。

プログラムの育てかた 現場で使えるリファクタリング入門

 

私は実は本は持っていません。日本語版も読んでいません。原書はWeb上からpdfでダウンロードできるので、それを読みました。以前、ブログで紹介したところはリンクが切れているようでしたが、以下からダウンロードできます。

この本でのポイントの一つは、一度にリファクタリングをしないということ。徐々に行っていくことです。このステップの解説がページを増やしていっています。

リファクタリングの手順は、既存のものはそのまま残し、置き換えるものを追加し、最後に既存のものを削除します。ただしこれは一気にやってもよく、もしバグが起きたときは、小さなステップを順々に行っていきます。

このカタログの中では、相反するものが出てきます。例えばExtract MethodとInline Methodは、一方はメソッドの抽出であり、もう一方は、メソッドを削除して呼び出し側に直接処理を書くというもので相反しています。これは、どちらが常に正しいというのではなく、状況に応じて適切に行うことが大切であるということを示しています。

http://mirrors.cn99.com/books/Refactoring.pdf

言語はCですが、以下のサイトも綺麗なソースを書くための参考になります。

Cプログラミング診断室--うつくしく健康なプログラムのために

「Refactoring: Improving the Design of Existing Code」メモ書き

Refactoring changes the programs in small steps. If you make a mistake, it is easy to find the bug.

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Temps are often a problem in that they cause a lot of parameters to be passed around when they don't have to be. You can easily lose track of what they are there for. They are particularly insidious in long methods. Of course there is a performance price to pay; here the charge is now calculated twice. But it is easy to optimize that in the rental class, and you can optimize much more effectively when the code is properly factored.

They are useful only within their own routine, and thus they encourage long, complex routines.

A while loop that takes a long time might impair performance. Many programmers would not do this refactoring simply for this reason. But note the words if and might. Until I profile I cannot tell how much time is needed for the loop to calculate or whether the loop is called often enough for it to affect the overall performance of the system. Don't worry about this while refactoring.

...

These queries are now available for any code written in the customer class. They can easily be added to the interface of the class should other parts of the system need this information.

If you must use a switch statement, it should be on your own data, not on someone else's.

Type information generally tends to be more volatile. If I change the movie type, I want the least ripple effect, so I prefer to calculate the charge within the movie.

This allows me to replace the switch statement by using polymorphism. Sadly it has one slight flaw.it doesn't work. A movie can change its classification during its lifetime. An object cannot change its class during its lifetime. There is a solution, however, the State pattern [Gang of Four].

When Should You Refactor?

The Rule of Three
Here's a guideline Don Roberts gave me: The first time you do something, you just do it. The second time you do something similar, you wince at the duplication, but you do the duplicate thing anyway. The third time you do something similar, you refactor.

Tip
Three strikes and you refactor.

 Refactor When You Add Function
 Refactor When You Need to Fix a Bug
 Refactor As You Do a Code Review

・  Programs that are hard to read are hard to modify.
・  Programs that have duplicated logic are hard to modify.
・  Programs that require additional behavior that requires you to
change running code are hard to modify.
・  Programs with complex conditional logic are hard to modify.

Don't publish interfaces prematurely. Modify your code ownership policies to smooth refactoring.

A clear sign of the need to rewrite is when the current code just does not work. You may discover this only by trying to test it and discovering that the code is so full of bugs that you cannot stablilize it. Remember, code has to work mostly correctly before you refactor.

In time I got more into this style of upfront design. Many people consider design to be the key piece and programming just mechanics. The analogy is design is an engineering drawing and code is the construction work. But software is very different from physical machines. It is much more malleable, and it is all about thinking. As Alistair Cockburn puts it, "With design I can think very fast, but my thinking is full of little holes."

One argument is that refactoring can be an alternative to upfront design. In this scenario you don't do any design at all. You just code the first approach that comes into your head, get it working, and then refactor it into shape. Actually, this approach can work. I've seen people do this and come out with a very well-designed piece of software. Those who support Extreme Programming [Beck, XP] often are portrayed as advocating this approach.

The interesting thing about performance is that if you analyze most programs, you find that they waste most of their time in a small fraction of the code. If you optimize all the code equally, you end up with 90 percent of the optimizations wasted, because you are optimizing code that isn't run much. The time spent making the program fast, the time lost because of lack of clarity, is all wasted time.

Chapter 3. Bad Smells in Code

Duplicated Code
Long Method
Large Class
Long Parameter List

Divergent Change
Divergent change occurs when one class is commonly changed in different ways for different reasons. If you look at a class and say, "Well, I will have to change these three methods every time I get a new database; I have to change these four methods every time there is a new financial instrument," you likely have a situation in which two objects are better than one.

Shotgun Surgery
Divergent change is one class that suffers many kinds of changes, and shotgun surgery is one change that alters many classes.

Feature Envy
a method that invokes half-a-dozen getting methods on another object to calculate some value.

there are several sophisticated patterns that break this rule. From the Gang of Four [Gang of Four] Strategy and Visitor immediately leap to mind. Kent Beck's Self Delegation [Beck] is another.

Data Clumps
The first step is to look for where the clumps appear as fields. Use Extract Class on the fields to turn the clumps into an object.

Primitive Obsession
People new to objects usually are reluctant to use small objects for small tasks, such as money classes that combine number and currency, ranges with an upper and a lower, and special strings such as telephone numbers and ZIP codes.

Switch Statements
Parallel Inheritance Hierarchies
Lazy Class

Speculative Generality
You get it when people say, "Oh, I think we need the ability to this kind of thing someday" and thus want all sorts of hooks and special cases to handle things that aren't required.

Temporary Field
Sometimes you see an object in which an instance variable is set only in certain circumstances. Such code is difficult to understand, because you expect an object to need all of its variables.

Message Chains
You see message chains when a client asks one object for another object, which the client then asks for yet another object, which the client then asks for yet another another object, and so on.

A a = o.getA();
B b = a.getB();
C c = b.getC();

Middle Man

Inappropriate Intimacy
Sometimes classes become far too intimate and spend too much time delving in each others'private parts.

Alternative Classes with Different Interfaces
occurs when the interfaces of two classes are different and yet the classes are quite similar. If you can find the similarities between the two classes, you can often refactor the classes to make them share a common interface

Incomplete Library Class

Data Class
Such classes are dumb data holders and are almost certainly being manipulated in far too much detail by other classes.In early stages these classes may have public fields.

Refused Bequest
Subclasses get to inherit the methods and data of their parents. But what if they don't want or need what they are given?

Comments
When you feel the need to write a comment, first try to refactor the code so that any comment becomes superfluous. If you need a comment to explain what a block of code does, try Extract Method. If the method is already extracted but you still need a comment to explain what it does, use Rename Method. If you need to state some rules about the required state of the system, use Introduce Assertion.

A comment is a good place to say why you did something. This kind of information helps future modifiers, especially forgetful ones.

Chapter 6. Composing Methods

Extract Method
Case: No Local Variables
I just cut, paste, and put in a call

Case: Using Local Variables
The easiest case with local variables is when the variables are read but not changed. In this case I can just pass them in as a parameter.

Case: Reassigning a Local Variable
It's the assignment to local variables that becomes complicated. In this case we're only talking about temps. If you see an assignment to a parameter, you should immediately use Remove Assignments to Parameters.

Inline Method

Inline Temp
You have a temp that is assigned to once with a simple expression, and the temp is getting in the way of other refactorings. Replace all references to that temp with the expression.
double basePrice = anOrder.basePrice();
return (basePrice > 1000)
  ↓
return (anOrder.basePrice() > 1000)
 Most of the time Inline Temp is used as part of Replace Temp with Query,

Replace Temp with Query
 The problem with temps is that they are temporary and local. Because they can be seen only in the context of the method in which they are used, temps tend to encourage longer methods, because that's the only way you can reach the temp. By replacing the temp with a query method, any method in the class can get at the information.

Introduce Explaining Variable
 Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose. I almost always prefer to use Extract Method if I can.

Split Temporary Variable
You have a temporary variable assigned to more than once, but is not a loop variable nor a collecting temporary variable. Make a separate temporary variable for each assignment.
Many other temporaries are used to hold the result of a long-winded bit of code for easy reference later. These kinds of variables should be set only once. That they are set more than once is a sign that they have more than one responsibility within the method. Any variable with more than one responsibility should be replaced with a temp for each responsibility. Using a temp for two different things is very confusing for the reader.

Remove Assignments to Parameters
The code assigns to a parameter.Use a temporary variable instead.

Replace Method with Method Object
You have a long method that uses local variables in such a way that you cannot apply Extract Method. Turn the method into its own object so that all the local variables become fields on that object. You can then decompose the method into other methods on the same object.

Substitute Algorithm
Replace the body of the method with the new algorithm.If you find a clearer way to do something, you should replace the complicated way with the clearer way.

Chapter 7. Moving Features Between Objects

Move Method
I move methods when classes have too much behavior or when classes are collaborating too much and are too highly coupled. By moving methods around, I can make the classes simpler and they end up being a more crisp implementation of a set of responsibilities.

Move Field
A field is, or will be, used by another class more than the class on which it is defined.

Extract Class
You have one class doing work that should be done by two. Create a new class and move the relevant fields and methods from the old class into the new class.

Inline Class
A class isn't doing very much. Move all its features into another class and delete it.

Hide Delegate
A client is calling a delegate class of an object. Create methods on the server to hide the delegate.
If a client calls a method defined on one of the fields of the server object, the client needs to know about this delegate object. If the delegate changes, the client also may have to change. You can remove this dependency by placing a simple delegating method on the server, which hides the delegate (Figure 7.1). Changes become limited to the server and don't propagate to the client.

Remove Middle Man
A class is doing too much simple delegation. Get the client to call the delegate directly.

Introduce Foreign Method
A server class you are using needs an additional method, but you can't modify the class. Create a method in the client class with an instance of the server class as its first argument.

Introduce Local Extension
A server class you are using needs several additional methods, but you can't modify the class. Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original.

Chapter 8. Organizing Data

Self Encapsulate Field
When it comes to accessing fields, there are two schools of thought. One is that within the class where the variable is defined, you should access the variable freely (direct variable access). The other school is that even within the class, you should always use accessors (indirect variable access).

Replace Data Value with Object
You have a data item that needs additional data or behavior. Turn the data item into an object.

Change Value to Reference
You have a class with many equal instances that you want to replace with a single object. Turn the object into a reference object.
You can make a useful classification of objects in many systems: reference objects and value objects. Reference objects are things like customer or account. Each object stands for one object in the real world, and you use the object identity to test whether they are equal. Value objects are things like date or money. They are defined entirely through their data values. You don't mind that copies exist; you may have hundreds of "1/1/2000" objects around your system. You do need to tell whether two of the objects are equal, so you need to override the equals method (and the hashCode method too).

Change Reference to Value
You have a reference object that is small, immutable, and awkward to manage. Turn it into a value object.
An important property of value objects is that they should be immutable. Any time you invoke a query on one, you should get the same result. If this is true, there is no problem having many objects represent the same thing. If the value is mutable, you have to ensure that changing any object also updates all the other objects that represent the same thing. That's so much of a pain that the easiest thing to do is to make it a reference object.

Replace Array with Object
You have an array in which certain elements mean different things. Replace the array with an object that has a field for each element.

Duplicate Observed Data
You have domain data available only in a GUI control, and domain methods need access. Copy the data to a domain object. Set up an observer to synchronize the two pieces of data.

Change Unidirectional Association to Bidirectional Change Bidirectional Association to Unidirectional
Bidirectional associations are useful, but they carry a price. The price is the added complexity of maintaining the two-way links and ensuring that objects are properly created and removed.
Lots of two-way links also make it easy for mistakes to lead to zombies: objects that should be dead but still hang around because of a reference that was not cleared. Bidirectional associations force an interdependency between the two classes. Any change to one class may cause a change to another. If the classes are in separate packages, you get an interdependency between the packages. Many interdependencies lead to a highly coupled system, in which any little change leads to lots of unpredictable ramifications.

Replace Magic Number with Symbolic Constant
You have a literal number with a particular meaning. Create a constant, name it after the meaning, and replace the number with it.

Encapsulate Field
There is a public field. Make it private and provide accessors.

Encapsulate Collection
A method returns a collection. Make it return a read-only view and provide add/remove methods.

 return Collections.unmodifiableSet(_courses);

Moving Behavior into the Class
I was concerned that moving this kind of behavior over to person would lead to a bloated person class. In practice, I've found that usually isn't a problem.

Replace Record with Data Class
You need to interface with a record structure in a traditional programming environment. Make a dumb data object for the record.

Replace Type Code with Class
A class has a numeric type code that does not affect its behavior. Replace the number with a new class.
Numeric type codes, or enumerations, are a common feature of C-based languages. With symbolic names they can be quite readable. The problem is that the symbolic name is only an alias; the compiler still sees the underlying number. The compiler type checks using the number not the symbolic name. Any method that takes the type code as an argument expects a number, and there is nothing to force a symbolic name to be used. This can reduce readability and be a source of bugs.

Replace Type Code with Subclasses
You have an immutable type code that affects the behavior of a class. Replace the type code with subclasses.

...switch statement I would prefer to avoid. But there is only one, and it is only used at creation.

Replace Type Code with State/Strategy
You have a type code that affects the behavior of a class, but you cannot use subclassing. Replace the type code with a state object.
This is similar to Replace Type Code with Subclasses, but can be used if the type code changes during the life of the object or if another reason prevents subclassing. It uses either the state or strategy pattern.
I assume this is an exciting and go-ahead company that allows promotion of managers to engineers. Thus the type code is mutable, and I can't use subclassing. My first step, as ever, is to self-encapsulate the type code:

Replace Subclass with Fields
You have subclasses that vary only in methods that return constant data. Change the methods to superclass fields and eliminate the subclasses.

Chapter 9. Simplifying Conditional Expressions

If you are working with code developed in a one exit point mentality, you often find control flags that allow the conditions to work with this rule. I don't follow the rule about one exit point from a method. Hence I use Replace Nested Conditional with Guard Clauses to clarify special case conditionals and Remove Control Flag to get rid of the awkward control flags.

Decompose Conditional
You have a complicated conditional (if-then-else) statement. Extract methods from the condition, then part, and else parts.
One of the most common areas of complexity in a program lies in complex conditional logic. As you write code to test conditions and to do various things depending on various conditions, you quickly end up with a pretty long method. Length of a method is in itself a factor that makes it harder to read, but conditions increase the difficulty. The problem usually lies in the fact that the code, both in the condition checks and in the actions, tells you what happens but can easily obscure why it happens.
Many programmers don't extract the condition parts in situations such as this. The conditions often are quite short, so it hardly seems worth it. Although the condition is often short, there often is a big gap between the intention of the code and its body. Even in this little case, reading notSummer(date) conveys a clearer message to me than does the original code. With the original I have to look at the code and figure out what it is doing. It's not difficult to do that here, but even so the extracted method reads more like a comment.

Consolidate Conditional Expression
You have a sequence of conditional tests with the same result. Combine them into a single conditional expression and extract it.
If you think the checks are really independent and shouldn't be thought of as a single check, don't do the refactoring. Your code already communicates your intention.
If the routine I'm looking at tests only the condition and returns a value, I can turn the routine into a single return statement using the ternary operator. So

  if (onVacation() && lengthOfService() > 10) return 1;
  else return 0.5;
becomes
  return (onVacation() && lengthOfService() > 10) ? 1 : 0.5;

Consolidate Duplicate Conditional Fragments
The same fragment of code is in all branches of a conditional expression. Move it outside of the expression.

Remove Control Flag
You have a variable that is acting as a control flag for a series of boolean expressions. Use a break or return instead.
The obvious way to deal with control flags is to use the break or continue statements present in Java. Even in languages with a break or continue, I usually prefer use of an extraction and of a return.

Replace Nested Conditional with Guard Clauses
A method has conditional behavior that does not make clear the normal path of execution. Use guard clauses for all the special cases.

double getPayAmount() {
	double result;
	if (_isDead) result = deadAmount();
	else {
		if (_isSeparated) result = separatedAmount();
		else {
			if (_isRetired) result = retiredAmount();
			else result = normalPayAmount();
		};
	}
	return result;
};

double getPayAmount() {
	if (_isDead) return deadAmount();
	if (_isSeparated) return separatedAmount();
	if (_isRetired) return retiredAmount();
	return normalPayAmount();
};

I often find that conditional expressions come in two forms. The first form is a check whether either course is part of the normal behavior. The second form is a situation in which one answer from the conditional indicates normal behavior and the other indicates an unusual condition.
These kinds of conditionals have different intentions, and these intentions should come through in the code. If both are part of normal behavior, use a condition with an if and an else leg. If the condition is an unusual condition, check the condition and return if the condition is true. This kind of check is often called a guard clause [Beck].

I often find I use Replace Nested Conditional with Guard Clauses when I'm working with a programmer who has been taught to have only one entry point and one exit point from a method. One entry point is enforced by modern languages, and one exit point is really not a useful rule. Clarity is the key principle: if the method is clearer with one exit point, use one exit point; otherwise don't.

Replace Conditional with Polymorphism
You have a conditional that chooses different behavior depending on the type of an object. Move each leg of the conditional to an overriding method in a subclass. Make the original method abstract.

Introduce Null Object
You have repeated checks for a null value. Replace the null value with a null object.
The essence of polymorphism is that instead of asking an object what type it is and then invoking some behavior based on the answer, you just invoke the behavior. The object, depending on its type, does the right thing. One of the less intuitive places to do this is where you have a null value in a field.
Our most common use of null objects is in the display of information. When we display, for example, a person, the object may or may not have any of perhaps 20 instance variables. If these were allowed to be null, the printing of a person would be very complex. Instead we plug in various null objects, all of which know how to display themselves in an orderly way. This got rid of huge amounts of procedural code.

 if (customer == null) plan = BillingPlan.basic();
 else plan = customer.getPlan();
    ↓
 plan = customer.getPlan();

The testing interface is an alternative to defining an isNull method. In this approach I create a null interface with no methods defined:
interface Null {}
I then implement null in my null objects:
class NullCustomer extends Customer implements Null...
I then test for nullness with the instanceof operator:
aCustomer instanceof Null
I normally run away screaming from the instanceof operator, but in this case it is okay to use it. It has the particular advantage that I don't need to change the customer class. This allows me to use the null object even when I don't have access to customer's source code.

Introduce Assertion
A section of code assumes something about the state of the program. Make the assumption explicit with an assertion.
Such assumptions often are not stated but can only be decoded by looking through an algorithm. Sometimes the assumptions are stated with a comment. A better technique is to make the assumption explicit by writing an assertion.
Assertions act as communication and debugging aids. In communication they help the reader understand the assumptions the code is making. In debugging, assertions can help catch bugs closer to their origin.
Beware of overusing assertions. Don't use assertions to check everything that you think is true for a section of code. Use assertions only to check things that need to be true. Overusing assertions can lead to duplicate logic that is awkward to maintain. Logic that covers an assumption is good because it forces you to rethink the section of the code. If the code works without the assertion, the assertion is confusing rather than helpful and may hinder modification in the future.
Always ask whether the code still works if an assertion fails. If the code does work, remove the assertion.

Chapter 10. Making Method Calls Simpler

Rename Method
Methods should be named in a way that communicates their intention. A good way to do this is to think what the comment for the method would be and turn that comment into the name of the method.

Remember your code is for a human first and a computer second. Humans need good names.

Add Parameter
Often you have other alternatives to adding a parameter. If available, these alternatives are better because they don't lead to increasing the length of parameter lists. Long parameter lists smell bad because they are hard to remember and often involve data clumps.

Remove Parameter
Check to see whether the class or superclass uses the parameter. If it does, don't do this refactoring.

Separate Query from Modifier
You have a method that returns a value but also changes the state of an object.

Create two methods, one for the query and one for the modification.
A good rule to follow is to say that any method that returns a value should not have observable side effects.

Parameterize Method
Several methods do similar things but with different values contained in the method body. Create one method that uses a parameter for the different values.

Replace Parameter with Explicit Methods
You have a method that runs different code depending on the values of an enumerated parameter. Create a separate method for each value of the parameter.

void setValue (String name, int value) {
  if (name.equals("height"))
    _height = value;
  if (name.equals("width"))
    _width = value;
}
 ↓
void setHeight(int arg) {
  _height = arg;
}
void setWidth (int arg) {
  _width = arg;
}

Replace Parameter with Explicit Methods is the reverse of Parameterize Method.
The caller has to decide what it wants to do by setting the parameter, so you might as well provide different methods and avoid the conditional.

The clarity of the explicit interface can be worthwhile even when the compile time checking isn't an advantage. Switch.beOn() is a lot clearer than Switch.setState(true), even when all you are doing is setting an internal boolean field.

Preserve Whole Object
You are getting several values from an object and passing these values as parameters in a method call. Send the whole object instead.

There is a down side. When you pass in values, the called object has a dependency on the values, but there isn't any dependency to the object from which the values were extracted. Passing in the required object causes a dependency between the required object and the called object. If this is going to mess up your dependency structure, don't use Preserve Whole Object.

Another reason I have heard for not using Preserve Whole Object is that when a calling object need only one value from the required object, it is better to pass in the value than to pass in the whole object. I don't subscribe to that view. One value and one object amount to the same thing when you pass them in, at least for clarity's sake (there may be a performance cost with pass by value parameters). The driving force is the dependency issue.

Replace Parameter with Method
An object invokes a method, then passes the result as a parameter for a method. The receiver can also invoke this method. Remove the parameter and let the receiver invoke the method.

Introduce Parameter Object
You have a group of parameters that naturally go together.
Replace them with an object.

Remove Setting Method
A field should be set at creation time and never altered.
Remove any setting method for that field.
If you don't want that field to change once the object is created, then don't provide a setting method (and make the field final).

Hide Method
A method is not used by any other class.
Make the method private.

Replace Constructor with Factory Method
You want to do more than simple construction when you create an object.
Replace the constructor with a factory method.

Encapsulate Downcast
A method returns an object that needs to be downcasted by its callers.
Move the downcast to within the method.

Replace Error Code with Exception
A method returns a special code to indicate an error.
Throw an exception instead.
Unix and C-based systems traditionally use a return code to signal success or failure of a routine. Java has a better way: exceptions. Exceptions are better because they clearly separate normal processing from error processing. This makes programs easier to understand.

To change this code to use an exception I first need to decide whether to use a checked or unchecked exception. The decision hinges on whether it is the responsibility of the caller to test the balance before withdrawing or whether it is the responsibility of the withdraw routine to make the check.

If testing the balance is the caller's responsibility, it is a programming error to call withdraw with an amount greater than the balance. Because it is a programming error that is, a bug--should use an unchecked exception[RuntimeException]. I should signal even more clearly by using an assertion.

If testing the balance is the withdraw routine's responsibility, I must declare the exception in the interface. That way I signal the caller to expect the exception and to take appropriate measures.

Replace Exception with Test
You are throwing a checked exception on a condition the caller could have checked first.
Change the caller to make the test first.
Exceptions should be used for exceptional behavior--behavior that is an unexpected error. They should not act as a substitute for conditional tests. If you can reasonably expect the caller to check the condition before calling the operation, you should provide a test, and the caller should use it.

Chapter 11. Dealing with Generalization

Pull Up Field
Two subclasses have the same field.
Move the field to the superclass.

Pull Up Method
You have methods with identical results on subclasses.
Move them to the superclass.

Pull Up Constructor Body
You have constructors on subclasses with mostly identical bodies.
Create a superclass constructor; call this from the subclass methods.

Push Down Method
Behavior on a superclass is relevant only for some of its subclasses.
Move it to those subclasses.

Push Down Field
A field is used only by some subclasses.
Move the field to those subclasses.

Extract Subclass
A class has features that are used only in some instances.
Create a subclass for that subset of features.

Extract Superclass
You have two classes with similar features.
Create a superclass and move the common features to the superclass.

Extract Interface
Several clients use the same subset of a class's interface, or two classes have part of their interfaces in common.
Extract the subset into an interface.
it is often useful to make the subset of responsibilities a thing in its own right, so that it can be made clear in the use of the system. That way it is easier to see how the responsibilities divide. If new classes are needed to support the subset, it is easier to see exactly what fits in the subset.

Collapse Hierarchy
A superclass and subclass are not very different.
Merge them together.

Form Template Method
You have two methods in subclasses that perform similar steps in the same order, yet the steps are different.
Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up.

Replace Inheritance with Delegation
A subclass uses only part of a superclasses interface or does not want to inherit data.
Create a field for the superclass, adjust methods to delegate to the superclass, and remove the subclassing.

Replace Delegation with Inheritance
You're using delegation and are often writing many simple delegations for the entire interface.
Make the delegating class a subclass of the delegate.

If you aren't using all the methods of the class to which you are delegating, you shouldn't use Replace Delegation with Inheritance, because a subclass should always follow the interface of the superclass. If the delegating methods are tiresome, you have other options. You can let the clients call the delegate themselves with Remove Middle Man. You can use Extract Superclass to separate the common interface and then inherit from the new class. You can use Extract Interface in a similar way.

Chapter 12. Big Refactorings

Tease Apart Inheritance
You have an inheritance hierarchy that is doing two jobs at once.
Create two hierarchies and use delegation to invoke one from the other.

Convert Procedural Design to Objects
You have code written in a procedural style.
Turn the data records into objects, break up the behavior, and move the behavior to the objects.
Often you're faced with the problem of procedure-like code that has to be more object oriented. The typical situation is long procedural methods on a class with little data and dumb data objects with nothing more than accessors. If you are converting from a purely procedural program, you may not even have this, but it's a good place to start.
We are not saying that you should never have objects with behavior and little or no data. We often use small strategy objects when we need to vary behavior. However, such procedural objects usually are small and are used when we have a particular need for flexibility.

Separate Domain from Presentation
You have GUI classes that contain domain logic.
Separate the domain logic into separate domain classes
The gold at the heart of MVC is the separation between the user interface code (the view, these days often called the presentation) and the domain logic (the model). The presentation classes contain only the logic needed to deal with the user interface. Domain objects contain no visual code but all the business logic. This separates two complicated parts of the program into pieces that are easier to modify. It also allows multiple presentations of the same business logic. Those experienced in working with objects use this separation instinctively, and it has proved its worth.

Extract Hierarchy
You have a class that is doing too much work, at least in part through many conditional statements.
Create a hierarchy of classes in which each subclass represents a special case.

The discipline of refactoring is hard to learn and easy to lose sight of, even if only for a moment. I still lose sight more often than I care to admit. I'll do two or three or four refactorings in a row without rerunning the test cases. Of course I can get away with it. I'm confident. I've practiced.
Boom! A test fails, and I can't see which of my changes caused the problem.
At this moment you will be mightily tempted to just debug your way out of trouble. After all, you got those tests to run in the first place. How hard could it be to get them running again? Stop. You are out of control, and you have no idea what it will take to get back in control by going forward.
Go back to your last known good configuration. Replay your changes one by one. Run the tests after each one.

Create a new method, and name it after the intention of the method (name it by what it does, not by how it does it).

At this point you may be wondering, "What happens if more than one variable needs to be returned?"
Here you have several options. The best option usually is to pick different code to extract. I prefer a method to return one value, so I would try to arrange for multiple methods for the different values. (If your language allows output parameters, you can use those. I prefer to use single return values as much as possible.)






この内容についてご意見をください

 
   役に立った    まあまあ    つまらない    難しい    疑問がある

コメント(質問・指摘・要望なんでもどうぞ)
  
メールアドレス: 

  

Copyright Dayan All rights reserved. Last Update: 2006.11.06 Create: 2006.11.06