for - みる会図書館


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

1. WORKING EFFECTIVELY WITH LEGACY CODE

EXTRACT METHOD refactoring t001 , it is even easier. All you have to do is select a portion of a method and make a menu selection. The t001 checks to see if that code can be extracted as a method and prompts you for the new method's name. E ェな 4 け M ビル od is a core technique for working with legacy code. You can use it tO extract duplication, separate responsibilities, and break down long methods. 419 Extract Method

2. WORKING EFFECTIVELY WITH LEGACY CODE

I DON'T HAVE MUCH TIME AND I HAVE T0 CHANGE IT This technique is called the decorator 々 4 な e ′れ . ・ We create objects Of a class that wraps another class and pass them around. The class that wraps should have the same interface as the class it is wrapping SO that clients don't know that they are working with a wrapper. ln the example, Loggi ngEmpl oyee is a deco- rator for Empl oyee. lt needs t0 have a pay() method and any other methods on Emp1 oyee that are used by the client. The Decorator Pattern Decorator allows you t0 build up complex behaviors by composing objects at runtime. For example, in an industrial process-control system, we might have a class called TooIContr011er with methods such as raise() , lower(), step(), on(), and off(). If we need to have additional things happen whenever we rai se() or lower() (things such as audible alarms to tell people to get out of the way), we could put that functionality right in those methods in the T001 朝 ntrol 1 er class. Chances are, though, that wouldn't be the end tO the enhancements. Eventually, we might need t0 10g the number of times we turn the controller on and 0 圧 We might alSO need tO notify Other controllers that are close by when we step SO that they can avoid stepping at the same time. The list Of things that we can dO along with our five simple operations (rai se, step, and Off) is endless, and it won't dO tO just create subclasses for each combination of things. The number of combinations of those behaviors could be endless. ・ The decorator pattern is an ideal fit for this sort of problem.When you use decorator, you create an abstract class that defines the set Of operations you need tO support. Then you create a subclass that inherits from that abstract class, accepts an instance Of the class in its constructor, and provides a bOdy for each Of those methods. Here is Wrap Class public void 0ff() { controller. 0ff() ; } public void on() { controller. on(); } public void step() { controller. step() ; } public void lower() { controller. lower(); } public void raise() { (0ntr011e「 . raise(); } this. controller = ( ontrolle 「 : publ ic T001Contr011erDecorator(T001Contr011 er control 1 (r) { protected T001 ( ont 「 011 er ( 0ntr011 er ; abstract class T001C0ntr01 IerDecorator extends T001Cont 「 01 ler that class for the T001 control 1 er problem:

3. WORKING EFFECTIVELY WITH LEGACY CODE

284 First Steps l'M CHANGING THE SAME CODE ALL OVER THE PLACE Okay, SO where are we now? We've removed so much duplication that we have just shells of classes. All of the functionality is in the Command class. ln fact, it makes sense tO wonder whether we really need separate classes for these tWO commands at all. What are the alternatives? We could get rid of the subclasses and add a static method to the Command class that allows us tO send a command: List arguments = new ArrayList() ; arguments. add("Mike") ; arguments. add("asdsad") ; Command. send(stream, 0X91 , arguments) ; But that would be a lot of work for clients. One thing is for sure: We do have tO send tWO different command chars, and we don't want the user tO have tO keep track of them. lnstead, we could add a different static method for each command that we want tO send: Command. SendAddEmpI oyee(stream , "Mike" " 122 E1m St" "FL", 1 0 の ; Command. SendLogin(stream, "Mike" "asdsad") ; But that would force all of our client code to change. Right now, there are many places ln our code where we construct AddEmpl oyeeCmd and Logi nCommand objects. Maybe we are better off leaving the classes the way that they are now. Sure, the subclasses are pretty tiny, but does that really hurt anything? Not really. Are we done?N ・ 0, there is one little thing that we need tO dO now, something we should've done earlier. We can rename AddEmployeeCmd t0 AddEmp10yeeCommand. That would make the names 0f the two subclasses consistent. We're less likely tO be wrong 、 vhen 、 use names conslstently. Abbreviation s Abbreviations in class and method names are problematic. They can be okay when they are used consistently, but in general, I don't like tO use them. One team I worked with attempted tO use the words 川 4 〃 ag に and 襯 4 れ age 襯 e れ一 in nearly every class name in the system. That naming convention help much, but what made it worse was the fact that they abbreviated 川 4 れ age and 川 4 れ age 川 e れ一 in an incredible number Of different ways. For example, classes named XXXXMgr, and others were named XXXXMngr. When you were ready to use a class, you actually had to 100k it up most 0f the time t0 see if you had the name right. More than 50 percent Of the time, I was wrong when I attempted t0 guess which suffix was used for a particular class.

4. WORKING EFFECTIVELY WITH LEGACY CODE

INTRODUCE INSTANCE DELEGATOR lntroduce lnstance Delegator People use Stat1C methods on classes for many reasons. One 0f the 1 第 ost common reasons is tO implement the S g / 可 0 れ Design ~ 4 なれ卩 72 ). Another common reason tO use StatiC methods iS tO create utility classes. Utility classes are pretty easy to find in many designs. They are classes that don't have any instance variables or lnstance methods. lnstead, they consist of a set Of static methods and constants. People create utility classes for many reasons. 、 'lost 0f the time, they are cre- ated when it is hard tO find a common abstraction for a set of methods. The Math class in the Java JDK is an example of this. lt has static methods for trigo- nometric functions (COS, sin, tan) and many Others. 、Ⅳ・ hen languages designers build their languages from objects "all the way down, " they make sure that numer1C primitives know hOW dO these things. For instance, you should be able to call the method si n ( ) on the object 1 or any other numeric object and get the right result. At the time Of this writing, Java does not support math methods on primitive types, SO the utility class is a fair solution, but it iS alSO a special case. ln nearly all cases, you can use plain 01d classes with instance data and methods tO dO your work. If you have static methods in your prOJect, chances are good that you won't run intO any trouble with them unless they contain something that is difficult tO depend on in a test. (The technical term for this iS 5 〃 c c 〃れ g ). ln these cases, you might wish that you could use an 0 を c ー 5e4 川卩の tO substitute ln some Other behavior when the static methods are called. What dO you do in this case? One thing that can dO is start tO introduce delegating instance methods on the class. When you do this, you have to find a way to replace the static calls with method calls on an object. Here is an example: public class BankingServices public static void updateAccountBa1ance(int userID, Money amount) { Here we've got a class that contalns nothing but static methods. l've shown only one here, but you get the idea. We can add an instance method to the class like this and have it delegate to the static method: public class BankingServices 369 lntroduce lnstance DeIegator

5. WORKING EFFECTIVELY WITH LEGACY CODE

304 Strateg y I NEED TO CHANGE A MONSTER METHOD AND I CAN'T WRITE TESTS FOR IT Break Out a Method Object sensing ″ 4 4 わん S are a very powerful t001 in our arsenal, but sometlmes you notice that you already have variables that would be ideal for sensing but they are local tO the method. If they were mstance variables, you could sense through them after a method runs. You can turn local variables intO instance variables, but, in many cases, that can be confusing. The state that you put there will be common only t0 the monster method and the methods that you extract from it. Although it will be reinitialized every time the monster method is called, it can be hard to understand what the variables will hold if you want t0 call methods that you've extracted independently. One alternative is B 尾 4 た〇 M ビ舫 od 〇わルは卩 3 の . This technique was first described by ward Cunningham, and it epitomizes the idea 0f an invented abstraction. When メ Ot1 break out a methOd object, you create a class whose only responsibility is t0 d0 the work 0f your monster meth0d. The parameters Of the method become parameters tO a constructor on the new class, and the COde Of the monster methOd can go intO a methOd named run or execute on the new class. When the COde has been moved tO the new class, we're a great position tO refactor. can turn the temporary variables in the method intO instance variables and sense through them as we break down the method. Breaking out a method Object is a pretty drastic move, but unlike introducing a sensing ″ 4 4 わ / ら the variables that you are using are needed for production. This allows you to build up tests that you can keep. See B 尾 4 た 0 町 Meth0d 〇わ卩 3 の for a detailed example. Strategy The techniques l've described in this chapter can help you break up monster methods for additional refactoring or feature addition. ThiS section contains some guidance about hOW tO make structural tradeoffs as you dO this work. Skeletonize Methods When you have a conditional statement and you are looking for places tO extract a method, you have tWO choices. You can extract the condition and the bOdy together, or you can extract them separately. Here is an example: if (margina1Rate() > 2 order. hasLimit()) { order. readjust(rateCa1cuIator. rateForT0day()) ;

6. WORKING EFFECTIVELY WITH LEGACY CODE

FIRST STEPS Looked like this: public void write(0utputStream outputStream) throws Exception { writeHeader(outputStream) ; writeB0dy(outputstream) ; writeF00ter(outputStream) ; N ・ ow we have a knob for writing headers and another for writing footers. We can add knobs as we need to, but it's nice when they happen naturally. Duplication removal is a powerful way of distilling a design. lt not only makes a design more flexible, but it alSO makes change faster and easier. Open/CIosed PrincipIe The Open/Closed Principle is a principle that was first articulated by Bertrand Meyer. The idea behind it is that code should be open for extension but closed to modifica- tion. What does that mean? lt means that when we have good design, we just don't have tO change COde much tO add new features. Does the code that we ended up with in this chapter exhibit these properties? Yes. We just looked at a number of change scenarios. ln many of them, very few methods had t0 change. ln some cases, we were able to add the feature just by subclassing. Of course, after subclassing, it iS important tO remove duplication (see p og 4 襯 / 〃 g わア D ~ 産の℃〃 c ビ卩の丿 for more information about how to add features by subclassing and integrate them by refactoring). When we remove duplication, our code often naturally starts to fall in line with the 〇〃 / C / os P ⅲ花ゆ . 287 First Steps

7. WORKING EFFECTIVELY WITH LEGACY CODE

254 Seeing Responsibilities THIS CLASS ls Too BIG AND I DON'T WANT IT TO GET ANY BIGGER Figure 20.5 shows the diagram after we've added a circle for the extend method: duration extend daiIyRate date customer fees Figure 20.5 extend 〃覊 5 d Ⅲ・酊わ〃 . If you've already read the chapters that describe effect sketching, you might notice that these んⅢ℃ 5 た訪ぉ 100k a lot like は s た可訪卩 55. ). Essentially, they are pretty close. The main difference iS that the arrows are reversed. ln 4 ー 4 s た e なわお , arrows point in the direction Of a methOd or variable that iS used by another methOd or variable. ln ビ〃し c お 5 た化わお , the arrow points toward methods or variables that are impacted by other methods and variables. These are two different, completely legitimate ways of lOOking at interactions in a system. 、 Fea れイ 7 ℃ 5 たビ化わ es are great for mapping the internal structure Of classes. Effect 5 た可 c わお卩 5 are great for reasoning forward from a point of change. ls it confusing that they 100k somewhat the same? NOt really. These sketches are disposable t001s. They are the sort 0f thing that you sit down and draw up with a partner for about 10 minutes before you make your changes. Afterward you throw them away. There is little value in keeping them around, so there is little likelihood that they will be confused with each other.

8. WORKING EFFECTIVELY WITH LEGACY CODE

384 Parameterize Method DEPENDENCY-BREAKING TECHNIQUES ln C + + , Java, C#, and many other languages, you can have two methods with the same name on a class, as long as the signatures are different. ln the example, we take advantage Of this and use the same name for the new parameterized methOd and the original method. Although this saves some work, at times it can be confusing. An alternative is tO use the type Of the parameter in the name Of the new methOd. For instance, in thiS case, we could keep run ( ) as the name Of the original method but call the new method runWithTestResult(TestResu1 t). As with Pa 川 e たビ CO れ 5 4 け 0 ( 379 ノ , Pa 川 e た e Method can allow cli- ents tO become dependent on new types that were used in the class before but were not present at the interface. If I think that this will become an lssue, I con- sider E ズ 4 4 れ d 〇怩な / んは 0 M ビ舫 0d ( 35 の instead. Steps To Pa 川可催た e Method, follow these steps: 1. ldentify the method that you want to replace and make a copy of it. 2. Add a parameter tO the method for the ObJect whose creation you are gomg tO replace. Remove the Object creatlon and add an assrgnment from the parameter to the variable that holds the object. 3. DeIete the body of the copied method and make a call to the parameter- ized method, using the Object creatlon expression for the original Object.

9. WORKING EFFECTIVELY WITH LEGACY CODE

lndex #include directives, 129 abbreviations, 284 access protection, subverting, Account, 120 , 364 ActionEvent class, 145 ACTIOReportFor, 108 141 Adapt Parameter, 142 , 326-329 adapting parameters, 326-329 addElement, 160 AddEmployeeCmd, 279 getBody, 280 write method, 274 adding features. See features, adding AGGController, 339-341 algorithms for changing legacy code, breaking dependencies, 19 finding test points, 19 identifying change points, 18 refactoring, 20 writing tests, 19 18 aliased parameters, getting classes 1ntO test harnesses, 133-136 analyzing effects, 167-168 API calls. 立ビ 4 な 0 libraries restructuring, 199-201 , 203-207 skinning and wrapping, 205-207 application architecture, preservmg, 215-216 conversation concepts, 224 Naked CRC, 220-223 telling story Of system, 216 ー 220 architecture Of system, preserving, 215-216 conversation concepts, 224 Naked CRC, 220-223 telling story of system, 216-220 automated refactoring monster methods, 294-296 tests, 46-47 automated tests, 185-186 characterization tests, 186-189 for classes, 189-190 heuristic for writing, 195 targeted testing, 190-194 Beck, Kent, 48 , 220 behavior, 5 preserving, 7 behavior Of COde. See charactenzatl()n tests 18 8 BindName method, 337 BondRegistry, 367 Brant, John, 45 Break Out Method Object, 137 , 330-336 monster methods, 304 breaking dependencies, 19-25 , 79-85 , 135 lnterception Points, 174-182 breaking up classes, 183 423

10. WORKING EFFECTIVELY WITH LEGACY CODE

50 Unit-Testing Harnesses TOOLS public void testNorma1Pay() { assertEquaIs(400, employee. getPay()) ; ln the Emp10yeeTest class, we have a special method named setUp. The setUp method is defined in TestCase and is run in each test Object before the test method is run. The setUp method allows us t0 create a set 0f objects that we'll use in a test. That set Of objects is created the same way before each test's execu- tion. ln the Object that runs testNormal pay, an employee created in setUp is checked tO see if it calculates pay correctly for one timecard, the one added in setUp. ln the object that runs testOvertime, an employee created in setUp for that Object gets an additional timecard, and there is a check tO verify that the second timecard triggers an overtime condition. The setUp method is called for each object 0f the class Empl oyeeTest, and each 0f those objects gets its own set 0f objects created via setUp. If you need t0 d0 anything special after a test finishes executing, you can override another method named tearDown, defined in TestCase. lt runs after the test method for each object. When you first see an xUnit harness, it is bound t0 100k a little strange. 嶬市 y dO test case classes have setUp and tearDown at 引嶬市 y can't we just create the ObjectS we need in the constructor?Well, we could, but remember what the test runner does with test case classes. lt goes tO each test case class and creates a set of objects, one for each test method. That is a large set Of objects, but it isn't SO bad if those objects haven't allocated what they need yet. By placing code in setUp tO create what we need just when we need it, we save quite a bit on resources. ln addition, by delaying setUp, we can alSO run it at a time when we can detect and report any problems that might happen during setup ・ CppUnitLite When I did the initial port of CppUnit, I tried to keep it as close as I could to JUnit. I figured it would be easier for people who'd seen the xUnit architecture before, so it seemed tO be the better thing tO dO. Almost immediately, I ran 1ntO a series Of things that were hard or impossible t0 implement cleanly in C + + because 0f differ- ences in 十十 and Java'S features. The primary 1SSue was C 十十 lack Of reflection. ln Java, you can hOld on tO a reference tO a derived class's methods, find methods at runtime, and SO on. ln C 十十 , you have tO write COde tO register the method you want tO access at runtime. AS a result, CppUnit became a little bit harder tO use and understand. You had tO write your own suite function on a test class SO that the test runner could run objects for individual methods.