Code that's easy to change is fun to work with. Code that's hard to change is stressful to work with.
one thing that particularly struck me was Kent's methodical way of continually reworking and improving the design of the system.
If I pick a large program, describing it and how it is refactored is too complicated for any reader to work through. (I tried and even a slightly complicated example ran to more than 100 pages.) However, if I pick a program that is small enough to be comprehensible, refactoring does not look like it is worthwhile.
simple data class.
The key to testing is running tests after every small change so when you mess up you don't have to look in many places to find the problem.
While refactoring you should focus on clarity, and then later focus on performance as a separate activity.
by applying Collection Closure Method
Why do I prefer to pass the length of rental to the movie rather than the movie type to the rental? It's because the proposed changes are all about adding new types. 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.
Replace Type Code with State/Strategy.
book-refactoring-ruby-edition#no-debugger All these changes were small steps. It seems slow to write it this way, but not once did I have to open the debugger, so the process actually flowed quite quickly. book-refactoring-ruby-edition#no-debugger
book-refactoring-ruby-edition#quick-iteration1The most important lesson from this example is the rhythm of refactoring: test, small change, test, small change, test, small change. It is that rhythm that allows refactoring to move quickly and safely. book-refactoring-ruby-edition#quick-iteration1
Smalltalk has a short compile-link-execute cycle, which makes it easy to change things quickly.
book-refactoring-ruby-edition#definitions1 2I'm always a little leery of definitions because everyone has his or her own, but when you write a book you get to choose your own definitions. In this case I'm basing my definitions on the work done by Ralph Johnson's group and assorted associates. book-refactoring-ruby-edition#definitions1 2
This second point leads to Kent Beck's metaphor of two hats. When you use refactoring to develop software, you divide your time between two distinct activities: adding function and refactoring.
book-refactoring-ruby-edition#what-hat-are-you-wearing you should always be aware of which hat you're wearing. book-refactoring-ruby-edition#what-hat-are-you-wearing
I make a point of trying to put everything I should remember into the code so I don't have to remember it.
book-refactoring-ruby-edition#kent-beck-not-great-good-habits It reminds me of a statement Kent Beck often makes about himself, "I'm not a great programmer; I'm just a good programmer with great habits." book-refactoring-ruby-edition#kent-beck-not-great-good-habits
I prefer UML diagrams and walking through scenarios with Class-Responsibility-Collaboration (CRC) cards.
When this happens, I say that the interface becomes a published interface (a step beyond a public interface).
So don't publish interfaces unless you really need to. This may mean modifying your code ownership rules to allow people to change other people's code to support an interface change. Often it is a good idea to do this with pair programming.
As you consider design alternatives, ask yourself how difficult it would be to refactor from one design into another. If it seems easy, don't worry too much about the choice, and pick the simplest design, even if it does not cover all the potential requirements. However, if you cannot see a simple way to refactor, then put more effort into the design. You should find such situations are in the minority.
With refactoring the emphasis changes. You still do up-front design, but now you don't try to find the perfect solution. Instead all you want is a reasonable solution.
You still think about potential changes; you still consider flexible solutions. But instead of implementing these flexible solutions, you ask yourself, "How difficult is it going to be to refactor a simple solution into the flexible solution?" If, as happens most of the time, the answer is "pretty easy," you just implement the simple solution.
corporate information systems
The second approach is the constant attention approach. With this approach every programmer, all the time, does whatever he or she can to keep performance high. This is a common approach and has intuitive attraction, but it does not work very well.
Since the early days of programming people have realized that the longer a procedure is, the more difficult it is to understand.
the real key to making it easy to understand small methods is good naming. If you have a good name for a method you don't need to look at the body.
whenever we feel the need to comment something, we write a method instead.
Long lists of parameters can be slimmed down with Introduce Parameter Object and Preserve Whole Object.
When a class is trying to do too much, it often shows up as too many instance variables. When a class has too many instance variables, duplicated code cannot be far behind.
book-refactoring-ruby-edition#objects-not-params1Most changes are removed by passing objects because you are much more likely to need to make only a couple of requests to get at a new piece of data. book-refactoring-ruby-edition#objects-not-params1
There is one important exception to making these changes. This is when you explicitly do not want to create a dependency from the called object to the larger object. In those cases, unpacking data and sending it along as parameters is reasonable, but pay attention to the pain involved. If the parameter list is too long or changes too often, you need to rethink your dependency structure.
A classic smell is a method that seems more interested in a class other than the one it actually is in. The most common focus of the envy is the data. We've lost count of the times we've seen a method that invokes half a dozen getting methods on another object to calculate some value.
The fundamental rule of thumb is to put things together that change together.
A common case of temporary field occurs when a complicated algorithm needs several variables. Because the implementer didn't want to pass around a huge parameter list (who does?), he put them in instance variables. But the instance variables are valid only during the algorithm; in other contexts they are just plain confusing. In this case you can use Extract Class with these variables and the methods that require them. The new object is a Method Object [Beck].
Often a better alternative is to see what the resulting object is used for. See whether you can use Extract Method to take a piece of the code that uses it and then Move Method to push it down the chain.
If it's time to leave home, apply Replace Inheritance with Delegation.
However, we can't deny that much of our programming skill is based on library classes so that nobody can tell whether we've forgotten our sort algorithms.
We do subclassing to reuse a bit of behavior all the time, and we find it a perfectly good way of doing business. There is a smell, we can't deny it, but usually it isn't a strong smell.
If you want to refactor, the essential precondition is having solid tests.
book-refactoring-ruby-edition#less-debugging Additionally, I find I hardly ever spend more than a few minutes debugging per day. book-refactoring-ruby-edition#less-debugging
Unless you have actually experienced the way it speeds programming, self-testing does not seem to make sense.
Writing the test also concentrates on the interface rather than the implementation (which is always a good thing). It also means you have a clear point at which you are done coding—when the test works.
uses the composite pattern
book-refactoring-ruby-edition#simple-feedback This simple feedback is essential to self-testing code. Without it you'll never run the tests often enough. With it you can run masses of tests and see the results immediately. book-refactoring-ruby-edition#simple-feedback
Again I'll mention that when I'm writing tests, I start by making them fail. With existing code I either change it to make it fail (if I can touch the code) or put an incorrect expected value in the assertion. I do this because I like to prove to myself that the test does actually run and the test is actually testing what it's supposed to (which is why I prefer changing the tested code if I can). This may be paranoia, but you can really confuse yourself when tests are testing something other than what you think they are testing.
For refactoring purposes, I count on the developer tests—the programmer's friend.
book-refactoring-ruby-edition#risk-driven Testing should be risk driven; remember, you are trying to find bugs now or in the future. So I don't test accessors that just read and write. Because they are so simple, I'm not likely to find a bug there. book-refactoring-ruby-edition#risk-driven
book-refactoring-ruby-edition#too-many-tests1 This is important because trying to write too many tests usually leads to not writing enough. I've often read books on testing, and my reaction has been to shy away from the mountain of stuff I have to do to test. This is counterproductive, because it makes you think that to test you have to do a lot of work. You get many benefits from testing even if you do only a little testing. The key is to test the areas that you are most worried about going wrong. That way you get the most benefit for your testing effort. book-refactoring-ruby-edition#too-many-tests1
book-refactoring-ruby-edition#boundary-condition Think of the boundary conditions under which things might go wrong and concentrate your tests there. book-refactoring-ruby-edition#boundary-condition
Notice how I'm playing the part of an enemy to code. I'm actively thinking about how I can break it. I find that state of mind to be both productive and fun. It indulges the mean-spirited part of my psyche.
In particular, it helps to think about error conditions and boundary conditions. That's another advantage for writing tests as you write code, or even before you write the production code.
There's always a risk that I'll miss something, but it is better to spend a reasonable time to catch most bugs than to spend ages trying to catch them all.
A difference between test code and production code is that it is okay to copy and edit test code.
book-refactoring-ruby-edition#how-big-of-steps1 I've written the mechanics in such a way that each step of each refactoring is as small as possible. I emphasize the safe way of doing the refactoring, which is to take small steps and test after every one. At work I usually take larger steps than some of the baby steps described, but if I run into a bug, I back out the step and take the smaller steps. The steps include a number of references to special cases. The steps thus also function as a checklist; I often forget these things myself. book-refactoring-ruby-edition#how-big-of-steps1
To improve the fluency of code I use Introduce Named Parameter. If I find later that the fluency the named parameter brings is no longer worth the complexity on the receiver, I can remove it with Remove Named Parameter.
I prefer short, well-named methods for several reasons. First, it increases the chances that other methods can use a method when the method is finely grained. Second, it allows the higher-level methods to read more like a series of comments. Overriding also is easier when the methods are finely grained.
If extracting improves clarity, do it, even if the name is longer than the code you have extracted.
Though parallel assignment can be used to return multiple values, I prefer to use single return values as much as possible. In this case, I try to do multiple extractions with each extraction only returning one value.
I commonly use Inline Method when someone is using too much indirection, and it seems that every method does simple delegation to another method, and I get lost in all the delegation.
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.
It should be stated that temps should not be introduced lightly.
book-refactoring-ruby-edition#inject-takes-getting-used-to1 If you need to do something in a loop that produces a single value, such as a sum, consider using the inject method. This can take a little getting used to. book-refactoring-ruby-edition#inject-takes-getting-used-to1