2.1 Bad Smell of Code (Part 1)

When you smell bad , you can consider refactoring . The so-called bad smell is the code in the code that does not seem to conform to the design, is difficult to understand and difficult to modify.
There is no precise measure of "when to refactor". No metric can match the intuition , and here are just a few indications when refactoring can be used to solve a problem. We must develop our own judgment about how many instance variables in a class are too big and how many lines of code in a function is too long. Roughly divided into 22 phenomena, the first 10 are introduced in this article, and the other 12 are introduced in the next article. Some of the practices mentioned here, such as extrat method, will also be introduced one by one later.

2.1 Duplicated Code (Duplicated Code)
The first of the bad smell ranks is Duplicated Code.   If you see the same program structure in more than one place, then you can be sure: try to combine them into one, the program will be better. First of all, many other bad smells are caused by duplication of code, and duplication of code often introduces bugs. When modifying or extending the code, it is necessary to maintain consistent changes. Missing one will lead to bugs.

(1) The simplest Duplicated Code is " two functions of the same class contain the same expression ". At this time, all you need to do is to use the Extract Method to extract the duplicated code, and then let both places be called to be extracted. The piece of code that came out.

(2) Another common situation is " two subclasses that are siblings contain the same expression ". To avoid this, just use Extract Method on both classes, then use Pull Up Method on the extracted code to push it into the superclass. If the code is only similar, not identical , then you have to use the Extract Method to split the similar parts and the different parts into a single function. Then you may find that you can use the Form Template Method to get a Template Method design pattern. If some functions do the same thing with different algorithms, you You can choose one of the clearer ones, and use Substitute Algorithm to replace the algorithms of other functions.

(3) If Duplicated Code appears in two unrelated classes, you should consider using Extract Class for one of them, and extract the duplicate code to In a separate class, and then use the new class in another class. However, it is also possible that the function where the duplicate code is located should only belong to one class, and the other class can only call it, or the function may belong to a third class , while the other two classes should refer to the second and third classes. You must decide where this function is most appropriate, and make sure it is placed so that it does not appear anywhere else.

2.2 Long Method (too long function)
Objects with short functions will live better and longer. The book "clean code" also emphasizes that a class and a method only do one thing, and functions should be as short and refined as possible. People who are not familiar with object-oriented technology , often feel that there are only endless delegates in the object program, no calculation at all. After living with such programs for a few years, you will realize how valuable these little functions are. The "indirection layer" can bring The whole benefit—explainability, sharing, choice—is backed by small functions. The

net effect is: you should factor functions more aggressively . We follow this principle: Whenever it feels like we need to explain something with a comment, we write it into a separate function and name it for its purpose (rather than how it is implemented) . We can do this for a set of code or even a short line of code. Even if the replaced function call action is longer than the function itself, as long as the function name explains its purpose, we should do it without hesitation. The point is not the function , but rather the semantic .

Ninety-nine percent of the time, to make the function smaller, just use the Extract Method. Find the parts of the function that fit together and distill them into a new function.

If you have a large number of parameters , they can hinder your function extraction. If you try to use the Extract Method, you will end up with many parameters and temporary variables as parameters passed to the new function that is extracted. , resulting in almost no readability improvement. At this point, you can often use Replace Temp with Query to eliminate these temporary elements. Introduce Parameter Object and Preserve Whole Object can make long parameter list more concise.

If you've done this and still have too many temporary variables and parameters, it's time to use our killer trick: Replace Method with Method Object.

How do you decide which piece of code to distill? A good trick : look for comments . They usually point out the semantic distance between the purpose of the code and the way it is implemented. If there is a line of comments in front of the code, it is a reminder: you can replace this code with a function, and you can name the function based on the comment. Even if it's just a single line of code, if it needs to be explained in a comment, it's worth distilling it into a standalone function.

Conditional expressions and loops are often also refined signals .You can use Decompose Conditional to process conditional expressions. As for loops, you should distill the loop and the code inside it into a separate function.

2.3 Large Class (too large class)
If you want to use a single class to do too many things, there are often too many instance variables in it. Once this happens, Duplicated Code will follow.

You can use Extract Class to combine several The variables are extracted together into a new class. When refining, you should select variables that are related to each other within the class and put them together. For example depositAmount and depositCurrency should probably belong to the same class. Usually if several variables within a class have the same prefix or suffix, it means there is an opportunity to extract them into a component. If the component is suitable as a subclass, you will find that Extract Subclass is often simpler.

Sometimes Classes don't use all instance variables at all times. If so, you might be able to use Extract Class or Extract Subclass multiple times.

Like "too many instance variables", having too much code inside a class is a source of code duplication, confusion, and eventual death. The easiest solution (remember, we like simple solutions) is to put the excess Things are destroyed inside the class. If there are five "hundred-line functions", and many of them have the same code, then maybe you can turn them into five "ten-line functions" and ten distilled "two-line functions" .

As with "having too many instance variables", a class with too much code is often suitable for using Extract Class and Extract Subclass. Here's a trick: first determine how clients will use them, then use the Extract Interface for each use way to extract an interface. This might help you see how to decompose the class.

2.
When we first started learning programming, the teacher taught us: pass everything that the function needs as parameters. This is understandable, because other than that, only global data can be selected, and global data is evil. Object technology changes that: if you don't have what you need, you can always ask another object to give it to you.

So, with an object, you don't have to pass everything the function needs as arguments, just pass it enough that the function can get what it needs from it. Most of what a function needs can be found in the function's host class. Functions in object-oriented programs usually have much shorter parameter lists than in traditional programs.

This is a good thing, because a parameter list that is too long is hard to understand , too many parameters can be inconsistent, difficult to use, and you have to modify it as soon as you need more data. If you pass an object to a function, most modifications will be unnecessary, since you'll likely get more data with just one or two more requests (within the function).

If a request to an existing object can replace a parameter, then you should activate the refactoring Replace Parameter with Method. Here, the "existing object" may be a field within the class to which the function belongs, or it may be another parameter. You can also use Preserve Whole Object to collect a bunch of data from the same object and replace them with that object. If some data lack a reasonable object attribution, use Introduce Parameter Object to create a "parameter object" for them.

There's an important exception here: sometimes you obviously don't want to create some kind of dependency between the "called object" and the "larger object". At this time, it is also reasonable to disassemble the data from the object as a separate parameter. But be aware of the cost it incurs. If the parameter list is too long or changes too frequently, you need to rethink your dependency structure.

2.5 Divergent Change
We want software to be more easily modifiable - after all, software is supposed to be "soft" anyway. Once changes are required, we want to be able to jump to a certain point in the system and make changes only there . Failing that, you're sniffing out one of two closely related pungent odors. Divergent Change

occurs when a class frequently changes in different directions for different reasons . When you look at a class and say: If a new database is added, I have to modify these three functions; if a new financial instrument appears, I have to modify these four functions. At this point it might be better to split the object into two, so that each object needs to be modified for only one change . Of course, you often only find this out after adding a new database or new financial instrument. All corresponding modifications to a change in the outside world should only occur in a single class, and everything within this new class should reflect that change. To do this, you should find all the changes for a specific reason, and then use Extract Class to extract them into another class.

2.6 Shotgun Surgery
Shotgun Surgery is similar to Divergent Change. But quite the opposite . If for every change you have to make many small changes within many different classes, the bad taste you face is Shotgun Surgery . If the code that needs to be modified is scattered 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 and Move Field to put all the code that needs to be modified into the same class. If There is no suitable class to house this code at the moment, so create one. Often you can use lnline Class to put a bunch of related behaviors into the same class. This may cause a small amount of Divergent Change. But you can handle it easily.

Divergent Change means "a class is affected by multiple changes", and Shotgun Surgery means "a change causes multiple classes to be modified accordingly". In both cases you'll want to organize your code so that "outside changes" tend to correspond one-to-one with "classes that need to be modified".

2.7 Feature Envy
The whole point of object technology is this: it's a technology that "wraps together data and the act of manipulating it." There is a classic smell: functions are more interested in a class than in their own class . The most common focus of this attachment is data. In countless experiences, we have seen that a certain number calls almost half a dozen value functions from another object in order to calculate a certain value . The cure is obvious: move the function to another location. You should use the Move Method to move it where it should go. Sometimes only a part of a function suffers from this attachment, in which case you should use the Extract Method to extract this part into a separate function, and then use the Move Method to take it to its dream home.

Of course, not all cases are so simple . A function often uses the functions of several classes, so where should it be placed? Our principle is: to determine which class has the most data used by this function , and then place this function and those data in the Together. If the function is first decomposed into several smaller functions by the Extract Method and placed in different places, the above steps will be easier to complete.

There are several intricate patterns that break this rule. Talking about this topic. GoF[Gangof Four) Strategy and Visitor immediately jumped into my mind. These patterns are used to combat the bad smell Divergent Change. The fundamental principle is: put together things that always change together. Data and the behavior that refers to that data always change together, but there are exceptions. If exceptions arise, we move those behaviors, keeping the changes in one place. Strategy and Visitor allow you to easily modify function behavior because they isolate a small amount of behavior that needs to be overridden. Of course it comes at the cost of "an extra layer of indirection".

2.8 Data Clumps
Data items are like children and like to hang out in groups. You can often see the same thing in many places Three or four items of data: the same fields in two classes, the same parameters in many function signatures. These data that are always tied together should really have their own objects. First find out that these data are in the form of fields , use Extract Class to extract them into a single object. Then turn your attention to the function signature, and use Introduce Parameter Object or Preserve Whole Object to lose weight for it. The immediate benefit of doing this is that many parameter columns can be shortened, simplifying function calls. Yes, don't worry about Data Clumps using only a subset of the fields of the new object, just replace two (or more) fields with the new object and you're worth the price.

A good judgement is: delete a lot of data an item. Does this make other data meaningless? If they no longer make sense, that's a clear signal: you should generate a new object for them.

Reduce the number of fields and parameters , of course removes some bad smells, but more importantly: once you have a new object, you have a chance to make the program smell a little better. Once you get the new object, you can start looking for Feature Envy, which
can help you point out Various program behaviors that can be moved into a new class. It doesn't take long for all classes to be fully valued in their little society.

2.9 Primitive Obsession (Primitive Obsession)
Most programming environments have two kinds of data: Structural types allow you to organize data into meaningful forms; Primitive types are the building blocks that make up structural types. Structs always come with a certain amount of overhead. They might represent tables in a database, and creating struct types just to do one or two things might seem too cumbersome.

A great value of objects is that they blur (or even break) the line between primitive data and bulky classes. You can easily write small classes that behave like built-in (primitive) types in the language. Java, for example, uses primitives to represent numbers, and classes to represent strings and dates, both of which are represented by primitives in many other programming environments.

Novices to object technology are often reluctant to use small objects for small tasks, such as the money class combining a number and currency, the range class consisting of a starting and ending value, special characters for phone numbers or zip codes, etc. string. You can use Replace Data Value with Object to replace the original data value with an object, so as to get out of the traditional cave and enter the hot object world. If the data value you want to replace is a type code, and it doesn't affect behavior, you can use Replace Type Code with Class to replace it. If you have conditional expressions associated with type codes, use Replace Type Code with Subclass or Replace Type Code with State/Strategy to handle them.

Use Extract Class if you have a set of fields that should always be kept together. If you see primitive data in the parameter column, try Introduce Parameter Object. If you are currently picking data from an array, use Replace Array with Object.

The use of objects to replace basic types here is not to replace a single basic type, but to use when several basic types are put together to make more sense. For example, "telephone number" is described by the basic number, area code, area, etc. , it is better to assemble into an object than to describe it with a primitive type field every time.

2.10 Switch Statements (Switch horror appeared)
One of the most obvious features of object-oriented programs is: less use of switch (or case) statements . Essentially, the problem with switch statements is repetition. You will often find the same switch statement scattered in different places (note this, not all switches should not exist) . If you want to add a new case clause to it, you have to find all the switch statements and modify them. The concept of polymorphism in object orientation can bring an elegant solution to this.

Most of the time, as soon as you see a switch statement, you should consider replacing it with polymorphism . The question is where does polymorphism come in? A switch statement is often chosen based on a type code, you want "a function or class associated with that type code", so you should use the Extract Method to extract the switch statement into a separate function, and then Use Move Method to move it to the class that needs polymorphism. At this point you must decide whether to use Replace Type Code with Subclasses or Replace Type Code with State/Strategy. Once this is done with the inheritance structure, you can apply Replace Conditional with Polymorphism.

If you just have some choice cases in a single function, and don't want to change them, polymorphism is a bit of a hack. Replace Parameter with Explicit Methods is a good choice in this case. If one of your selection criteria is null, try Introduce Null Object.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327007293&siteId=291194637