Refactoring-bad taste of code

Source: Internet
Author: User
Tags repetition

1. Duplicated code (repeated code)

Duplicated Code is the first among the stinks. If you see the same program structure in more than one location, the program will become better if you are sure to combine them into one.

The simplest duplicated code is [two functions in the same class contain the same expression]. At this time, you need to use extractmethod (110) to extract duplicate code, and then let the two locations call the extracted code.

Another common case is that [two mutual sibling (sibling) subclasses contains the same expression]. To avoid this situation, you only need to use extract method (110) for both classes, and then use pull up method (332) for the extracted code to push it into superclass. If the code is similar but not completely the same, you must use extractmethod (110) to cut apart the similarity and difference parts to form a separate function. Then you may find that you can use Form
Template Method (345) gets a template method design pattern. If some functions use different algorithms to do the same thing, you can choose a clearer one and use substitute algorithm (139) to replace the algorithms of other functions.

If duplicated code appears in two unrelated classes, you should consider using extract class (149) for one of them and extract the duplicate code into an independent class, then use the new class in another class. However, the function where the repeated code is located may indeed belong to only one class, and the other class can only call it, or this function may belong to the third class, the other two classes should reference the third class. You must decide where the function is most suitable and ensure that it will not appear anywhere else after it is placed.

2. Long method (long function)

Objects with [Short functions] (short methods) will be better and longer-lived. People who are not familiar with object-oriented technology often feel that the object program only has endless delegation (delegation) and does not perform any computing at all. After living with such programs for several years, you will know how valuable these small functions are. [Indirect layer] All benefits-interpreting, sharing, and selection-are supported by small functions.

A long time ago, programmers realized that the longer the program, the more difficult it is to understand. In early programming languages, [subprogram calling actions] requires additional overhead, which makes small method less desirable, modern OO language almost eliminates the [extra overhead of function calling actions] in the process. But the reader of the code is still a lot effort-consuming, because he must often switch the context to see what the subroutine has done. Some development environments allow users to see two functions at the same time, which helps you save some trouble, but the key to making small method easy to understand is a good name. If you can give a good name to a function, the reader can understand the function's role through the name, without having to look at what is written in it.

The final result is that you should break down functions more aggressively. We follow the principle that every time we feel that we need to annotate something, we write what needs to be explained into an independent function, it is named after its purpose (rather than implementation method. We can do this for a group or even just one line of code. Even if the replaced function call action is longer than the function itself, we should not hesitate to do so as long as the function name can explain its purpose. The key lies not in the function length, but in the semantic distance between the function [What to Do] and [How to Do.

In 99%, to reduce the function, you only need to use the extract method. Find the appropriate components in the function and extract them to form a new function.

If a function contains a large number of parameters and temporary variables, they will hinder your function extraction. If you try to use the extract method, many of these parameters and temporary variables will eventually be passed to the extracted new function, resulting in almost no readability improvement. Ah yes, you can use replace temp with query (120) frequently to make too long parameter columns more concise.

If you have already done so and there are still too many temporary variables and parameters, you should come up with our killer: replace method with method object (135 ).

How can I determine which code to extract? A good trick is to look for comments. They usually point out the signal of [semantic distance between code usage and implementation techniques. If there is a line of comments in the Code preface, it reminds you that you can replace this code with a function, and you can name this function based on the comments. Even if there is only one line of code, if it needs to be annotated, it is worth extracting it into an independent function.

Conditional and loop are often extracted signals. You can use decompose conditional (238) for conditional processing. As for the loop, you should extract the loop and its code into an independent function.

3. Large Class (over-category)

If you want to use a single class to do too many things, there will usually be too many instance variables in it. Once this happens, the duplicated code comes one after another.

You can extract several variables into the new class using extract class (149. When refining, you should select variables related to each other in the class and keep them all the time. For example, "depositamount" and "depositcurrency" may belong to the same class. Generally, if several variables in the class have the same prefix or suffix, this means they have the opportunity to be extracted into a certain component. If this component is suitable as a subclass, you will find that extract subclass (330) is often relatively simple.

Sometimes the class does not use all instance variables at all times. If so, you may use extract class or extract subclass multiple times.

Like [too many instance variables], if there are too many code in the class, it is also an excellent breeding place for [] Code repetition, chaos, and death. The simplest solution is to remove the excess from the class. If there are five [Hundred-line functions], many of them have the same code, maybe you can convert them into five [ten-row functions] and ten [double-row functions].

Like [having too many instance variables], if a class has too much code, it is often suitable for extract class (149) and extractsubclass (330 ). Here is a useful technique: first determine how the client uses them, and then use extract interface (341) to use an interface for each. This may help you understand how to break down this class.

If your large class is a GUI class, you may need to move data and behavior to an independent domain object. You may need to keep duplicate data on both sides and synchronize the data. Duplicate observed data (189) tells you how to do this. In this case, especially if you use the old AWT component, you can remove the GUI class and replace it with the swing component.

 

 

4. long parameter list (too long parameter column)

At the beginning of learning programming, the teacher taught us to pass in all the things required by the function as parameters. This is understandable, because we can only select global data, and global data is evil. Object technology has changed this situation, because if you don't have what you need, you can always call another object for you. Therefore, with an object, you do not have to pass everything required by the function as a parameter, you just need to give it enough to let the function get everything you need. Most of the things required by a function can be found in the host class of the function. The parameter columns of functions in object-oriented programs are usually much shorter than those in traditional programs.

This is a good phenomenon, because too long parameter columns are hard to understand. Too many parameters may cause inconsistency and are not easy to use. Once you need more data, You have to modify it. If you pass an object to a function, most modifications are unnecessary, because you may only need to add one or two requests (within the function) to get more data.

If you [send a request to an existing object] to obtain a copy of the data originally in the parameter column, you should activate the reconstruction criterion replace parameter with method (292 ). The preceding object may be a value field in the class to which the function belongs, or it may be another parameter. You can also use preserve whole object (288) to collect a bunch of data from the same object and replace them with this object. If some data lacks a reasonable object ownership, you can use introduce
Parameter object (295) creates a [parameter object] for them.

An important exception exists here. Sometimes you obviously do not want to create a certain dependency between the [called object] and [large object. At this time, it is reasonable to split the data from the object as a separate parameter. However, pay attention to the cost. If the parameter columns are too long or change too frequently, you need to reconsider your dependency structure.

5. Divergent change (divergent change)

We hope the software can be more easily modified-after all, the software should have been [Soft. Once modification is required, we hope to fall down to a certain point of the system and only make modifications here. If you cannot do this, you will find one of the two closely related pungent flavors.

If a class often changes in different directions for different reasons, divergent change appears. When you look at a class and say, "Well, if you add a new database, I have to modify these three functions. If a new financial tool appears, I have to modify these four functions ", at this time, it may be better to divide this object into two parts, so that each object can be modified only because of a change. Of course, you can only find this point after you join a new database or financial tool. All corresponding modifications to an external change should only occur in a single class, And all content in this new class should be reversed. To this end, you should find all the changes caused by a specific reason, and then use Extract
Class (149) extracts them into another class.

6. Shotgun surgery (volume modification)

Shotgun surgery is similar to divergentchange, but the opposite is true. If you encounter a change, you must make many small changes in many different classes to respond to it. The bad taste you face is shotgun surgery. If the code to be modified is spread around, it is not only difficult to find them, but also easy to forget an important modification.

In this case, you should use move method (142) and move field (146) to put all the code to be modified into the same class. If there is no suitable class to place the code, create one. Generally, you can use inline class (154) to put a series of related behaviors into the same class. This may cause a small number of divergent
Change, but you can handle it easily.

Divergent change refers to [a class is affected by multiple changes], and shotgunsurgery refers to [a change causes multiple classes to be modified]. In both cases, you may want to organize the code to achieve an ideal situation where [external changes] and [classes to be changed] present a one-to-one relationship.

7. Feature envy (Attachment)

All the main points of object technology are: This is a technology that [encapsulates data and the operational behaviors on it. The classic smell is that a function is more interested in a class than in its host class. The most common focus of this kind of sentiment is data. Countless times of experience, we can see that a function calls almost half a dozen value functions from another object to calculate a value. Therapy: Move this function to another location. You should use the move method (142) to move it to the desired place. Sometimes only a part of the functions suffer from this attachment. In this case, you should use extract.
Method (110) extracts this part into independent functions, and then uses move method (142) to bring it to its dream home.

Of course, not all situations are that simple. A function often uses several classes, so where should it be placed? Our principle is: Determine which class has the most data [used by this function], and then put this function and that data together. If extract method (110) is used to split the function into smaller functions and place them in different locations, the above steps are easier to complete.

Several Complicated and sophisticated models have broken this rule. Speaking of this topic, the streategy and visitor of the [four giants] immediately jumped into my mind, and Kent Beck's self delegation was also popular. These modes are used to combat bad taste divergent change. The most fundamental principle is to put things that are always changing together. The behavior of [data] and [referencing this data] Always changes, but there are exceptions. If an exception occurs, we will move those actions and keep [changes only happen together]. Strategy and visitor allow you to easily modify function behavior because they isolate a small number of behavior that requires overwriting-and of course pay the price of [more than one indirect.

8. Data clumps (data dashboard)

Data items are like children: they like to stay together in groups. You can often see three or four identical data items in many places: the same value fields in two classes and the same parameters in many function signatures. These [always tied together with the data] should be put into their own objects. First, locate the vertices in the value form of the data and extract them into an independent object using extractclass (149. Then focus on the function signature header and use introduce parameter object (295) or preserve
Whole object (288) for it to lose weight. The direct advantage of doing so is that many parameter columns can be shortened to simplify function call actions. Yes, because data clumps only cares about part of the value range of the new object, as long as you replace two (or more) value ranges with the new object, you will return the fare.

A good way to judge is to delete a large amount of data. Is other data meaningless? If they no longer have inquiries, this is a clear signal: You should create a new object for them.

Reducing the number of Value domains and parameter numbers can certainly lead to some bad taste, but more importantly, once you have a new object, you will have the opportunity to make the program a scent. After you get the new object, you can start looking for the feature envy, which helps you to point out various program behaviors in the [New Class. It doesn't take too long. All classes will make full use of their productivity in their small society.

9. Primitive obsession (basic type paranoid)

In most programming environments, there are two types of data: schema types allow you to organize data into meaningful forms, and basic types constitute structural blocks. The structure will always incur additional costs. They are a bit like tables in the database, or something that is not worth the candle.

An extremely valuable thing of objects comes early: they blur the boundaries between basic data and larger volumes of classes. You can easily compile small classes that are of the same type as the built-in language. For example, Java represents a value with a basic type, while the core class represents a string and a date. These two types are represented with a basic type in many other programming environments.

New users of object technology usually use small objects in small tasks, such as the money class that combines numeric and currency values, the range class that contains a starting value and an ending value, the telephone number or zip code, etc.. You can use replace data value with object (175) to replace an existing data value with an object to get out of the traditional cave and enter the hot object world. If the data value to be replaced is type code and does not affect behavior, you can use replace
Type codewith class (218) to replace it. If you have a condition that depends on this type code, you can use replace type code withsubclass (213) or replace type code with State/Strategy (227) to process it.

If you have a set of values that should always be put together, you can use extract class (149 ). If you see basic data in the parameter column, try introduce parameter object (295 ). If you find that you are selecting data from an array, you can use replace array with object (186 ).

10. Switch statements)

One of the most obvious features of object-oriented programs is that they use less switch (or case) statements. Essentially, the problem with switch statements lies in repetition. You often find that the same switch statement is distributed in different locations. To add a new case clause for it, you must locate all switch statements and modify them. Oriented polymorphism can bring about an elegant solution.

Most of the time, you should consider replacing the switch statement with polymorphism. The problem is where polymorphism occurs? The switch statement is often selected based on the type code. You need [functions or classes related to the Type Code]. Therefore, you should use extract method (110) to extract the switch statement into an independent function, and then move it to the class that requires polymorphism with move method (142. In this case, you must decide whether to use replace.
Type Code withsubclasses (223) or replace type code with State/Strategy (227 ). After completing the inheritance structure, you can use replaceconditional with polymorphism (255.

If you just select examples in a single function and you don't want to change them, [polymorphism] is a bit cool. In this case, replaceparameter with explicit methods (285) is a good choice. If one of your selection conditions is null, try introduce Null Object (260 ).

11. Parallel inheritance hierarchies (Equality inheritance system)

Parallel inheritance hierarchies is actually a special case of shotgun surgery. In this case, each time you add a subclass to a class, you must add a subclass to the other class. If you find that the class name prefix of an inheritance system is exactly the same as the class name prefix of another inheritance system, it smells bad.

The general strategy to eliminate this repeatability is to let the entity of an inheritance system refer to the entity of another inheritance system. If you make persistent efforts to use the move method and move field, you can eliminate the inheritance system of the terminals involved.

12. Lazy class (redundant class)

Every class you create has to be understood and maintained by someone, which is costly. If the income of a class is not worth its identity, it should disappear. This often occurs in projects: a class is originally worthy of its own identity, but its identity is reduced by its identity, so it does not do so much work; or the developer planned some changes beforehand, and add a class to make these changes, but the changes do not actually happen. Whatever one of the above reasons, please make this class solemn. If some subclass do not do enough work, try collapse hierarchy (344 ). For almost useless components, you should use inline
Class (154) against them.

13. Speculative generality)

This makes us very sensitive and bad. The name of the person is Brian Foote. When someone says, "oh, I think we need to do this one day" and tries to handle unnecessary things in various hooks and special circumstances, this bad taste appears. The results often make the system more difficult to understand and maintain. If all devices are used, it is worth doing so; if not, it is not worth doing. An unusable device will only block your path, so let's move it.

If one of your abstract classes does not have much function, use collapse hierarchy. Non-essential delegation can be removed using inline class. If some parameters of a function are used, you can implement the rename method to make it more realistic.

If the only user of a function or class is test cases, the speculative generality is bad. If you find such functions or classes, delete them together with their test cases. However, if they are used to help test cases detect legitimate functions, you must leave them with a knife.

14. Temporary Field (confusing temporary value range)

Sometimes you will see an object where an instance variable is set only for a specific situation. Such code is hard to understand, because you usually think that an object needs all its variables at all times. If a variable is not used, you can guess that the purpose of the variable will drive you crazy.

Please use extract class to create a home for this poor loneliness, and then put all the Code related to this variable into this new home. You may also use introduce null object to create a null object when the [variable is invalid], so as to avoid writing [conditional Code].

If the class has a complex algorithm that requires several variables, it may lead to bad taste of temporaryfield. Because the implementer does not want to pass a long string of parameters, he puts these parameters into the value range. However, these Value domains are only valid when the algorithm is used. In other cases, they are only confusing. At this time, you can use extract class to extract these variables and their related functions into an independent class. The new extracted object is a method object.

15. Message chains (over-coupled message chains)

If you see that the user asks for another object from an object, then the latter asks for another object, and then retrieves another object ...... This is the message chain. In actual code, you may see a long string of getthis () or a long string of temporary variables. This means that the customer will be closely coupled with the sailing structure during the search process. Once the relationship between objects changes, the client must modify the relationship accordingly.

In this case, you should use hide delegate. You can perform this refactoring in different locations of message chain. In theory, you can reconstruct any object in the message chain, but in this way, all the intermediary objects are often converted into middleman. Generally, a better choice is: first observe what the final object obtained by the message chain is used to see if extract method can extract the code using this object into an independent function, then use movemethod to push this function into message chain. If an object on this chain has multiple customers planning to sail the rest of this route, add a function to do this.

Some people regard any function chain as a bad thing. We don't think so. Well, our general representatives are well-known, at least in this case.

16. middle man (intermediate transfer Person)

One of the basic features of an object is encapsulation-hiding its internal details from the external world. Encapsulation is often accompanied by delegation. For example, if you ask the supervisor if he has time to attend a meeting, he will entrust the message to his notebook before he can answer you. Well, you don't have to know whether the supervisor uses a traditional album, an electronic album, or a secretary to record his appointment.

However, delegation may be used excessively. You may see that half of the functions of a class interface are delegated to other classes, which means excessive use. Here you should use remove middle man to directly deal with the owner object. If there are only a few functions in this way, you can use inlinemethod to "inlining" them into the caller. If the middleman has other internal behaviors, you can use replace delegation with inheritance to convert it into the subclass responsible for the object. In this way, you can expand the behavior of the original object without having to bear so many delegate actions.

17. Inappropriate intimacy (relationship)

Sometimes you will see that the two classes are too close and spend too much time exploring the private components of each other. If this happens between two [persons], we don't have to be the defender; but for classes, we want them to strictly abide by the rules.

Just like an ancient lover, classes that are too popular must be broken up. You can use the move method and move field to draw a line between them to reduce the number of neighborhood. You can also check whether changebidirectional Association to unidirectional is used to break one class into another. If the two classes are really keen, you can use extract class to extract the two into a safe place, so that they can use the new class openly. Alternatively, you can try to use hide delegate to let another class share their thoughts.

Inheritance often leads to excessive intimacy, because subclass's understanding of superclass always exceeds the subjective desire of superclass. If you think it is time to let this child live alone, please use replaceinheritance with delegation to let it leave the inheritance system.

18. Alternative classes with different interfaces (similar class)

If two functions do the same thing but have different signatures, use rename method to rename them. But this is often not enough. Please use movemethod repeatedly to move some actions into the classes until the two protocols are consistent. If you need to repeat the code to complete this, you may be able to use extract superclass to redeem your sins.

19. Incomplete Library Class (imperfect Library Class)

Reuse is often regarded as the ultimate goal of objects. We think this is an overestimation. However, there is no denying that many programming technologies are built on library classes. No one dares to say that we have forgotten the sorting algorithm.

The library classes builders do not have the capabilities of crowdsourced security testing, so we cannot blame them for this. After all, we can figure out the design of the system almost always when the system is about to be built. Therefore, the task of the library builder is really arduous. The trouble is that the library format is often not good enough, and it is often impossible for us to modify the classes so that it can do what we want to do. Does this mean that tactics that have passed practical tests, such as movemethod, are useless today?

Fortunately, we have two tools dedicated to this situation. If you only want to modify one or two functions in the library classes, you can use introduce foreignmethod. If you want to add a large number of additional actions, you must use introduce local extension.

20. Data class (naive data class)

The so-called data class refers to the fact that they have some value domains and functions used to access these Value domains, which has nothing to do with it. Such classes are just [non-talking data containers], and they are almost always manipulated by other classes excessively. These classes may have a public value in the early stages, so you should use encapsulate field to encapsulate them immediately before others notice them. If these classes contain the value range of the container class, you should check whether they have been properly encapsulated; if not, use encapsulate collection to encapsulate them. For the Value domains that should not be modified by other classes, use remove
Setting method.

Then, find out where these [value/set value] functions are used by other classes. Try to move the call behavior to the data class using the move method. If the entire function cannot be moved, use the extract method to generate a function that can be moved. Soon you will be able to use hide method to hide these [value/set value] functions.

Data class is like a child. It is a good starting point, but if you want them to participate in the work of the entire system as the [Adult] objects, they must take responsibility.

21. Refused bequest (rejected gifts)

Subclasses should inherit the functions and data of superclass. But what if they do not want to or do not need inheritance? They get all the gifts, but only a few of them are available!

Traditionally, this means that the design of the inheritance system is wrong. You need to create a new brother for this subclass, and then use the push down method and push down field to push all unused functions to the brother. In this way, the superclass only holds all things shared by subclasses. You will often hear the suggestion that all superclasses should be abstract.

Since we use the [traditional saying], a slightly derogatory term, you can guess that we do not recommend that you do this. At least we do not recommend that you do this every time. We often use subclassing to reuse some behaviors and find that this can be well applied to daily work. This is also a bad taste. We do not deny it, but the smell is usually not strong. Therefore, if refused bequest causes confusion and problems, follow the traditional advice. But you don't have to think you have to do that every time. In, the bad taste is very light and it is worth ignoring.

If subclass reuses superclass behavior (Implementation) but is unwilling to support superclass interfaces, the bad taste of refused bequest will become strong. We don't mind rejecting the implementation of the superclass, but we don't agree if we refuse to inherit the superclass interface. However, even if you do not want to inherit interfaces, do not modify the inheritance system randomly. You should use replace inheritance with delegation to achieve your goal.

22. Comments (too many comments)

Don't worry. We don't mean you shouldn't write comments. In terms of smell, comments are not bad; in fact, they are still bad. We need to mention comments here because it is often used as a deodorant. This is often the case where you see a piece of code with long comments and find that these comments exist because the code is terrible. It is surprising that this situation occurs many times.

Comments can show us the various bad tastes mentioned earlier in this chapter. After finding the bad taste, we should first remove the bad taste with a variety of reconstruction methods. After the completion, we often find that the comment has become redundant, because the Code has clearly explained everything.

If you need to comment out what a piece of code has done, try the extract method. If you need to comment out the specifications required by some systems, try introduceassertion.

If you do not know what to do, this is a good time to use annotations. In addition to describing future plans, annotations can also be used to mark areas that you are not sure about. You can write down yourself in the comment [Why do you do this?]. This type of information can help future modifier, especially those who are forgetful.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.