about - みる会図書館


検索対象: WORKING EFFECTIVELY WITH LEGACY CODE
152件見つかりました。

1. WORKING EFFECTIVELY WITH LEGACY CODE

Chapter 11 I Need to Make a Change. What Methods Should I Test? 、、 need tO make some changes, and we need tO write c わ arac た 4 〃 0 〃 s な 卩 86 丿 to pin down the behavior that is already there. Where should we write them? The simplest answer is tO write tests for each method that we change. But is that enough? lt can be if the code is simple and easy to understand, but in legacy code, often all bets are 0 圧 A change in one place can affect behavior someplace else; unless we have a test in place, we might never know about it. When I need to make changes in particularly tangled legacy code, I often spend time trying tO figure out where I should write my tests. This involves thinking about the change I am going to make, seeing what it will affect, seeing what the affected things will affect, and so on. This type of reasoning is nothing new; people have been dOing it since the dawn Of the computer age. Programmers Sit down and reason about their programs for many reasons. The funny thing is, we don't talk about it much. 嶬 just assume that everyone knows hOW tO dO it and that dOing it is just part Of being a programmer. Unfortunately, that doesn't help us much when we are confronted with terribly tangled code that goes far beyond our ability to reason easily about it. We know that we should refactor it tO make it more understandable, but then there is that issue Of testing again. If we don't have tests, how dO we know that we are refactoring correctly? I wrote the techniques in this chapter to bridge the gap. Often we do have to Reasoning About Effects reason about programs ln non-trivial ways tO find the best places tO test. 151 in SOft 、 vare, there is some associated chain Of effects. For instance, if I change ln the industry, we don't talk about this often, but for every functional change ー Need to Make a Change

2. WORKING EFFECTIVELY WITH LEGACY CODE

224 Conversation Scrutiny 、 'lY APPLICATION HAS NO STRUCTURE Conversation Scrutiny ln legacy code, it's tempting tO avoid creating abstractions. When l'm looking at four or five classes that have about a thousand lines Of COde aplece, l'm not thinking about adding new classes as much as l'm trying t0 figure out what has tO change. Because we are SO distracted when we're trying tO figure out these things, Often we miss things that can grve us additional ideas. Here's an example. I was working with several members Of a team once, and they were going through the exercise 0f making a large chunk 0f code executable from severalthreads. The COde was rather complicated and there were several opportunities for deadlock. We realized that if we could guarantee that resources were locked in and unlocked in a particular order, we could avoid deadlock in the code. We started to look at how we could modify the code to enable this. AII the while, we were talking about this new locking policy and figuring out hOW tO marntarn counts ln arrays tO enable it.When one Of the Other programmers started tO write the policy code inline, I said, "Wait, we're talking about a locking policy, right? Why don't we create a class called LockingP01icy and maintain the counts in there? We can use method names that really describe what we are trying tO do, and that will be clearer than COde that bumps counts ln an array. The terrible thing is that the team wasn't inexperienced. There were some Other very good-looking areas Of the COde base, but there is something mesmer- izing about large chunks 0f procedural code: They seem t0 beg for more. Listen tO conversatlons about your design. Are the concepts you re usrng ln conversation the same as the concepts in the COde ~ I wouldn't expect them all tO be. Software has tO satisfy stronger constraints than just being easy tO talk about, but if there isn't a strong overlap between conversatlon and COde, it's important tO ask why. The answer IS usually a mixture Of tWO things: The COde hasn't been allowed tO adapt tO the team's understanding, or the team needs tO understand it differently. ln any case, being very tuned t0 the concepts people naturally use t0 describe the design is powerful. When people talk about design, they are trying t0 make 0ther people understand them. Put some 0f that under- standing in the COde. ln this chapter, l've described a couple 0f techniques for uncovering and communlcating the architecture Of large exlsting systems. Many Of the tech- niques are also perfectly good ways 0f working out the design 0f new systems. Design is design, regardless 0f when it happens in the development cycle. One

3. WORKING EFFECTIVELY WITH LEGACY CODE

TELLING THE STORY OF THE SYSTEM are the most essential things about the system. Next, you pick the next most important things t0 say about the system. You keep gomg until you've said just about everything important about the core design Of the system. When you start to do this, you'll notice an odd feeling. TO really convey the system architecture that briefly, you have t0 simplify. You might say, "The gate- way gets rule sets from the active database," but as you say that, part Of you might be screaming, "NO! The gateway gets rule sets frOI れ the active database, but it alSO gets them from the current working set. ' ' When you say the simpler thing, it kind 0f feels like you are lying; you just aren't telling the whole story. But you are telling a simpler story that describes an easier-to-understand archi- tecture. For instance, why does the gateway have tO get rule sets fror れ 1 れ ore than one place? Wouldn't it be simpler if it was unified? pragmatic considerations Often keep things from getting simple, but there is value in articulating the simple view. At the very least, it helps everyone under- stand what would've been ideal and what things are there as expediencies. The other important thing about this technique is that it really forces you tO think about what is important in the system, the most important things tO communl- cate ~ Teams can go on SO far when the system they work on is a mystery tO them. ln an Odd way, having a simple story Of hOW a system works just serves as a roadmap, a way 0f getting your bearing as you search for the right places t0 add features. lt can alSO make a system a 10t less scary. On your team, tell the story Of the system Often, Just SO that you share a view. Tell it in different ways. Trade Off whether one concept is more lmportant than another. AS you consider changes tO the system, you'll notice that some changes fall more in line with the story. That is, they make the briefer story feel like less of a lie. If you have to choose between two ways 0f doing something, the story can be a good way tO see which one will lead tO an easier-to- understand system. Here iS an example Of this sort Of story telling in action. Here'S a session dis- cussing JUnit. lt does assume that you know a little bit about the architecture Of JUnit. If you don't, take a little while to look at JUnit's source code. You can download it from www.junit.org/ What is the architecture of JUnit? JUnit has two primary classes. The first is called Test, and the other is called TestResult. Users create tests and run them, passing them a TestResult. When a test fails, it tells the TestResult about it. People can then ask the TestResult for all of the failures that have occurred. 217 TellingtheStory Of the System

4. WORKING EFFECTIVELY WITH LEGACY CODE

SIMPLIFYING EFFECT SKETCHES 嶬 hhen we remove tiny P1eces 0f duplication, we 0ften end up getting effect sketches with a smaller set Of endpoints. ThiS Often translates intO easler testing decisions. Effects and Encapsulation One Of the often-mentioned benefits Of Object orientation IS encapsulation. Many times when I show people the dependency-breaking techniques ⅲ this book' they point out that many 0f them break encapsulation. That's true. Many 0f them d0. Encapsulation IS important, but the reason why it iS important iS 川 0 ビ important. Encapsulation helps us reason about our COde. ln well-encapsulated there are fewer paths tO fOllOW as you try tO understand it. For instance, if we add another parameter tO a constructor tO break a dependency as we dO in the Pa ー 4 襯ビ′たビ CO 〃 s 〃は 0 ( 3 792 refactoring, we have one more path tO fOllOW when we are reasoning about effects. Breaking encapsulation can make reasoning ab011t our COde harder, but it can make it easier if we end up with good explanatory tests afterward. ・ we test cases for 竄 class, can use them tO reason about our COde more directly.We can also write new tests for any questions that Ⅵ℃ might have about the behavior of the code. Encapsulation and test coverage aren't always at OddS' but when they are, I bias toward test coverage. Often it can help me get more encapsulation later. Encapsulation isn't an end in itself; it iS a t00 ー for understanding. 、 hhen we need tO find out where tO write our tests, it's important tO know what can be affected by the changes we are making. We have t0 reason about effects. we can dO this sort Of reasoning informally or ln a more ngorous way with little sketches, but it pays t0 practice it. ln particularly tangled code, it is one 0f the 0 司 y skills we can depend upon in the process 0f getting tests in place. 171 Simplifying Effect S ketches

5. WORKING EFFECTIVELY WITH LEGACY CODE

generatelndex getEIementCount REASONING FORWARD addElement elements getEIement Figure 11.8 E 〃ビけ s た訪 0 わ e InMemoryDi rectory 日 5. But is there any chance we've missed anything?What about superclasses and subclasses? If any data in InMemoryDi rectory is public, protected, or package- scoped, a method in a subclass could use it in ways that we won't know about. ln this example, the instance variables in lnMemoryDi rectory are private, SO we don't have tO worry about that. When you are sketching effects, make sure that you have found all of the clients of the class you are examining. If your class has a superclass or subclasses, there might be Other clients that you haven't considered. Are we done? WeII, there is one thing that we've glossed over completely. We're using the Element class in the directory, but it isn't part of our effect sketch. Let's 100k at it more closely. When we call generatelndex, we create an 臼 ement and repeatedly call addText on it. Let's look at the code for EI ement: public class E1ement { private String name; private String text = public E1ement(String name) { thiS . name = name; 161 Reasoning Forward

6. WORKING EFFECTIVELY WITH LEGACY CODE

Robert C. Martin Series The miSS10n 0f this serles is tO improve the state 0f the art 0f software craftsmanship. The bOOks in this serres are technical, pragmatlc, and substantial. The authors are highly experienced craftsmen and professionals dedicated t0 writing about what actually works in practice, as opposed to what might work in theory. You will read about what the author has done, not what he thinks you should do. If the book is about programming, there will be lots of code. If the b00k is about managing, there will be lots of case studies from real projects. These are the bOOks that all serious practitioners will have on their bookshelves. These are the books that will be remembered for making a difference and for guiding professionals tO become true craftsman. Ma れ ag g Agile Projects Sanjiv Augustine ス g / ル Es 〃襯酣 g 4 〃 d 円 4 〃〃 g Mike Cohn Wo 黻 g E が“〃怩ル / 舫 Legacy Code Michael C. Feathers Agile JavaTM: Crafting CO ル / ル立 - D 怩〃 D 卲 0 々川 e 厩 Jeff Langr Agile P 〃 c ゆ / ぉ , Pa な邵〃 s , 4 〃 d ~ たお C# Robert C. Martin and Micah Martin Agile SO ″ル 4 D ビレ elo 々襯ビ〃た P 〃じゅ / ぉ , Pa なビ川 5 , 4 〃 d ~ c 〃 c お Robert C. Martin UML お 0 JavaTM Programmers Robert C. Martin お″ for D 卲ビ / 0 々 g So ″ル 4 : 孖 4 川ビル 0 黻 for g d 花 5 な Rick Mugridge and Ward Cunningham Agile SO ″ル 4 D 0 々川厩ル / 舫 SCR UM Ken Schwaber and Mike Beedle E 川ビ So ″ル 4 E 〃 g 〃 g : A Ha 〃ホ 0 〃 A 々々 roa 訪 DanieI H. Steinberg and Daniel Ⅳ Palmer For more informatlon, visit http://www.prenhallpofessional.com/martinseries

7. WORKING EFFECTIVELY WITH LEGACY CODE

XVI PREFACE What do you think about when you hear the term g40 じ 0d2 If you are at all like me, you think of tangled, unintelligible structure, code that you have to change but don't really understand. You think 0f sleepless nights trying t0 add in features that should be easy t0 add, and you think 0f demoralization, the sense that everyone on the team IS SO sick Of a COde base that it seems beyond care, the sort 0f code that you just wish would die. Part 0f you feels bad for even thinking about making it better. lt seems unworthy Of your efforts. That definition of legacy code has nothing t0 d0 with who wrote it. Code can degrade in many ways, and many 0f them have nothing t0 d0 with whether the COde came frOI れ another team. ln the industry, g40 じ 0 is often used as a slang term for difficult-to-change code that we don't understand. But over years Of working with teams, helping them get past serious COde problems, l've arrived at a different definition. TO me, g40 code is simply code without tests. l've gotten some grief for this definition. What do tests have to do with whether code is bad? To me, the answer is straightforward, and it is a point that I elaborate throughout the book: COde without tests is bad COde. lt doesn't matter hOW well written it is; it doesn't mat- ter hOW pretty or object-oriented or well-encapsulated it iS. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse. You might think that this is severe. What about clean code? If a code base is very clean and well structured, isn't that enough? Well, make no mistake. I love clean code. I love it more than most people I know, but while clean code is good, it's not enough. Teams take senous chances when they try tO make large changes without tests. lt is like dOing aerial gymnastics without a net. lt requires incredible skill and a clear understanding 0f what can happen at every step. Knowing precisely what will happen if you change a couple 0f variables is Often like knowing whether another gymnast is going tO catch your arms after you come out Of a somersault. If you are on a team with COde that clear, you are in a better position than most programmers. ln my work, l've noticed that teams with that degree 0f clarity in all 0f their code are rare. They seem like a statistical anomaly. And, you know what? If they don't have supporting tests, their code changes still appear t0 be slower than those 0f teams that d0. Yes, teams dO get better and start tO write clearer code, but it takes a long time for Older COde tO get clearer. ln many cases, it will never happen com- pletely. Because 0f this, I have no problem defining legacy code as code without tests. lt is a good working definition, and it points tO a SOluti011. l've been talking about tests quite a bit SO far, but this bOOk is not about test- ing. This book is about being able t0 confidently make changes in any code

8. WORKING EFFECTIVELY WITH LEGACY CODE

220 Naked CRC MY APPLICATION HAS NO STRUCTURE JUnit has two primary classes. The first is called Test, and the other is called TestResult. Users create tests and run them, passing them a TestResult. ・ When a test runs, it passes informatlon about the test run tO the TestResult. people can then ask the TestResult for information about all Of the test runs. ls that better? FrankIy, I like the original, the version that described record- ing failures. TO me, it is one 0f the core behaviors 0f JUnit. If we change the code SO that TestResults record the number Of assertions run, we'd still be lying a bit, but we're already glossing over the Other information that we send from tests tO test results. The alternative, putting the responsibility for running a bunch of cases and building a report from them on TestCase, would be a bolder lie: We aren't talking about this additional responsibility of TestCase at all. We're better Off having tests report the number Of assertions run as they execute. Our first story is generalized a little bit more but at least lt is still substantially true. That means that our changes are falling more in line with the architecture of the system. Naked CRC ln the early days of object orientation, many people struggled with the issue of design. lt's hard tO get used tO Object orientation when most Of your program- ming experience is in the use 0f procedurallanguages. Simply put, the way that you think about your COde iS different. I remember the first time someone tried to show me an object-oriented design on a piece of paper. I looked at all the shapes and lines and heard the description, but the question that I kept wanting t0 ask was "Where's mai n()? Where is the entry point for all of these new object things?" I was bewildered for a little while, but then it started to click. The problem wasn't just mine, though. lt seemed like most of the industry was struggling with the same issues at roughly the same time. Frankly, every day people new tO the industry confront these issues when they encounter ObJect- oriented code for the first time. lt's still happening ・ ln the 198 Os, Ward Cunningham and Kent Beck were dealing with this issue. They were trying t0 help people start t0 think about design in terms of objects. At the time, Ward was using a t001 named Hypercard, which allows you to cre- ate cards on a computer display and form links among them. Suddenly, the insight was there. Why not use real index cards tO represent classes? lt would

9. WORKING EFFECTIVELY WITH LEGACY CODE

XIV FOREWORD Legacy code. The phrase strikes disgust in the hearts 0f programmers. lt con- jures images 0f slogging through a murky swamp 0f tangled undergrowth with leeches beneath and stinging flies above. lt conJures Odors Of murk, slime, stag- nancy, and offal. Although our first joy 0f programming may have been intense, the misery of dealing with legacy code is 0ften sufficient t0 extinguish that flame. Many Of us have tried tO discover ways tO 々 e レビれ一 code from becoming leg- acy. ・ we've written bOOks on principles, patterns, and practices that can help programmers keep their systems clean. But Michael Feathers had an insight that many Of the rest Of us missed. PreventIon is imperfect. Even the most disciplined development team, knowing the best principles, using the best patterns, and fol- lOWing the best practices will create messes from time tO time. The rot still accu- mulates. lt's not enough tO try tO prevent the rot—you have tO be able tO レビ S lt. That's what this bOOk is about. lt's about reversing the rot. lt's about taking a tangled, opaque, convoluted system and slowly, gradually, piece by piece, step by step, turning it intO a simple, nicely structured, well-designed system. lt's about reversing entropy. Before you get t00 excited, I warn you; reversmg rot IS not easy, and it's not quick. The techniques, patterns, and t001s that Michael presents in this b00k are effective, but they take work, time, endurance, and じ 4 巳 This bOOk is not a magic bullet. lt won't tell you hOW tO eliminate all the accumulated rot in your systems overnight. Rather, this bOOk describes a set Of disciplines, concepts, and attitudes that you will carry with you for the rest Of your career and that ル / ″ わゆ ) 0 〃川 5 川 s 舫酊 g 4 〃ア degrade 5 川 5 舫酣 g d 〃 4 〃ァ み〃々 ro レ e. 犬 0 C. Ma れ 29 れら 2004

10. WORKING EFFECTIVELY WITH LEGACY CODE

TELLING THE STORY OF THE SYSTEM な舫酣 4 胛 No. Actually, Test is an interface. There is a class called TestCase that implements Test. Users subclass TestCase and then write their tests as public void methods that start with the word test in their subclass. The TestSuite class uses reflection to build up a group of tests that can be run in a single call tO TestSuite ' s run method. 嶬み e can go further, but what l've shown SO far gives a sense Of the technique. start out by making a brief description. When we simplify and rip away detail tO describe a system, we are really abstracting. Often when we force ourselves tO commumcate a very simple View Of a system, 、 can find new abstractlons. If a system isn't as simple as the simplest story we can tell about it, does that mean that it's bad? NO. lnvariably, as systems grow, they get more complicated. The story gives us guidance. Suppose that we were going tO add a new feature tO JUnit. We want tO gen- erate a report Of all the tests that don't call any assertions when we run them. What options dO we have given what was described in JUnit? One option is to add a method to the TestCase class called bui 1dUsageReport that runs each method and then builds up a report of all of the methods that don't call an assert method. Would that be a good way of adding this feature? What would it do to our story? Well, it would add another little "lie of omis- SIOn" from our briefest description Of the system: JUnit has two primary classes. The first is called Test, and the other is called TestResult. Users create tests and run them, passing along a TestResult. When a test fails, it tells the TestResult about it. People can then ask the TestResu1 t for all of the failures that have occurred. lt seems that Tests now have this completely different responsibility: generat- lng reports, 、Ⅵ℃ never mentlon. What if we went about adding the feature in a different way? 嶬 could alter the interaction between TestCase and TestResul t SO that TestResul t gets a count Of the number Of assertlons run 、 vhenever a test runs. Then Ⅵ℃ can make a report- building class and register it with TestResult as a listener. HOW does that impact the story Of the system? lt could be a good reason tO generalize it a little. Tests don't just tell TestResu1ts about the number 0f failures; they also tellthem about the number Of errors, the number Of tests run, and the number Of assertions run. We could change our brief story t0 this: 219 TeIIing the Story Of the System