preface
Why read this book
I started reading this book because I spent more than six months refactoring the project code.
After reading the codes handed over by many people, the project code as a whole will find that the code is very laborious to read, and the reusability and extensibility are poor. In addition, the logic of the code is too convoluted and confusing. Looking at code like this, I feel like I’m racing inside.
When reconstructing, they will scold and change, which challenges humanity and gives people a very bad experience.
When you’re done refactoring a project, you’ll find many common problems with bad code.
You write your own rules without any rules or specifications at all, whereas good code has a set of rules and very clear specifications. It is because there is no concept or theory in mind that I write like this.
Reminds me of Tolstoy’s words: happy people are alike, unhappy people are unhappy in their own way. When it comes to writing code, good code is all alike, and bad code has its faults.
Where is the theoretical support and methodology? It is acquired through study and reading. So I find this book to read, in order to deeply study this theory.
It took five hours to read the book and three days off work to put together the ideas. In general, most of the ideas in this book have been applied and put into practice in this refactoring project, so when you see these ideas and methods, you will understand more deeply.
tips
-
The development of
-
Refine the short functions and keep the single responsibility principle. Functions and data should be packaged in well-formed units. And name the function after its purpose
-
The function should keep the change in one place, and if not consider separating out the changing parts
-
Several classes in the same part, should consider refining/decomposition functions, and inheritance. The code is too fragmented, so consider merging the code
-
Reduce the coupling of classes, methods, and range, and consider moving and refining related content
-
Reduce temporary variables; Temporary expressions can take final into account. Complex expressions should consider creating temporary variables
-
Call a function with too many arguments, should consider refining the class or using a class representation
-
Redundant code and comments should be removed
-
-
test
-
Take small steps and test frequently
-
Threshold test
-
Continue to enrich test cases
-
-
Refactoring record
- The refactoring process, motivation, cases, and diagrams should be documented
The book summary
-
Bad code smell
-
Duplicate code
-
Q: Redundant code
-
R: Extract the same code into a method/class, separating the same and different parts
-
-
Too long to function
-
Q: It’s too long to understand
-
R: Extract and decompose into different short functions and name them with their purpose processes
-
R: Conditional expressions and loops can also extract functions
-
R: Following the single responsibility principle, code with different functions is extracted into different classes
-
-
A broad categories
-
Q: Too much code in a class can cause code duplication and confusion
-
R: Refine classes and subclasses, refining different interfaces for each usage method
-
-
Too long parameter column
-
Q: Too many parameters are hard to understand and may be inconsistent. Subsequent function calls require many parameters
-
R: Only the required parameters are passed
-
R: Encapsulated entity classes
-
-
Divergent variation
-
Q: A class is subject to many, many variations
-
R: Changes should only occur in a single class in response to changes in different environments
-
-
Scattershot modification
-
Q: Every time you encounter a change, you need several different classes to make small changes in response
-
R: Move classes and methods, putting all the code you need to change into the same class
-
-
Attachment bond
-
Q: The function is more interested in a class than it is in the host class
-
Example: a function calls almost half a dozen value functions from another function object to evaluate a value
-
R: Move the function where it needs to go. Rule of movement: Whichever class has the most data used by this function, put that function and that data together
-
-
Data mud pie
-
Q: If two classes have the same value field and the same parameters in many function signatures, you can extract your own class
-
R; The same attributes are extracted into a class
-
-
Basic type don’t be paranoid
-
Q: Using large objects adds performance overhead
-
R: Write small objects, such as the Money class, representing ranges
-
R: Replaces data values that would otherwise exist separately with objects
-
-
Switch thriller
-
Q: Switch expressions bring repetition
-
R: Use polymorphism
-
-
Parallel inheritance system
-
Q: Every time you add a subclass to a class, you must add a subclass to another class. You find that the class name prefix of the inherited system is exactly the same as the class name prefix of the other system, and you need to separate the two systems
-
R: Let entities of one inheritance system refer to another
-
-
Lazy class
-
Q: Classes are worthless/redundant
-
R: Kill the extra classes directly
-
-
Talk about the future
-
Q: You think you’ll need it someday and do some unnecessary processing
-
R: Remove unnecessary parameters, test classes, etc
-
-
Confusing temporal range
-
Q: An instance variable is only used for a particular situation, but not always
-
R: Use refining functions to extract these variables and their related functions into a separate class
-
-
Over-coupled message chains
-
Q: One object requests another, forming a message chain. Too much coupling
-
R: See what purpose you end up with, and then see if you can refine the function
-
-
The middleman
-
Q: Over-extraction of functions/delegate functions
-
R: Deal directly with the person you are responsible for, eliminating unnecessary transfers
-
-
Inappropriate intimacy
-
Q: The two classes are too close, so it takes a lot of time to explore the class relationship
-
R: Move a method or class to draw a line
-
R: Extract common points into a class
-
-
Classes of the same kind
-
Q: Two functions do the same thing but have different signatures
-
R: Rename classes according to their purpose, or extract shared parts directly
-
-
Imperfect library classes
-
Q: Library classes are not sufficient
-
R: Reference external methods, or reference local extensions
-
-
Naive data classes
- Useless classes
-
A rejected bequest
-
Q: Functions that are not needed in a subclass should not be inherited
-
R: Extract the required parent class, and then inherit another class
-
-
Too many comments
-
Q: Too many comments can lead to misunderstanding
-
R: Remove unnecessary comments
-
-
-
Build test architecture
-
Self test
-
Test frequently
-
Each class should have a main() for testing, which might not be easy to implement
-
-
JUnit testing framework
-
The test suite
- The test code
-
The test case
-
A separate test class
-
Start the test by letting it fail, such as error expectations, failure/exception values
-
Unit testing versus functional testing
-
-
The test equipment
-
-
Adding more tests
-
Watch what the class does, and then test it for every possible failure of any one of those functions
-
Testing should be risk-driven
-
Look for boundary conditions: Look for special situations that could lead to failure
-
Continue to enrich test cases
-
A test class contains another test class
-
The test should focus on the areas that are prone to error
-
-
-
List of refactoring
-
Refactored record format
-
The name of the
- Build a refactoring vocabulary
-
A brief summary
-
The context in which refactoring works, what it does
-
A short sentence describing the problem this refactoring can help with
-
A short statement about what to do
-
A sketch to show a simple example before and after refactoring; Code can be presented as well as UML diagrams
-
-
motivation
-
Why is this refactoring necessary
-
When should YOU not refactor
-
-
practice
-
Briefly explain how to do this refactoring step by step
-
You can recall it in the future, quickly remember how to do that
-
Keep each step short
-
To safely refactor, take very small steps and test them after each step
-
-
sample
-
A very simple example of how this refactoring works
-
Help explain the basics of refactoring
-
-
-
Looking for reference points
-
How mature are these refactoring criteria
-
Take small steps and test frequently
-
Introduce design patterns
-
-
-
Reorganize your functions
-
Refining function
-
Motivation: An excessively long function, or a piece of code that requires comments to understand
-
Function length is not the problem, but the semantic distance between function name and function body is the key
-
practice
-
Create a new function named after what it does
-
Small granularity, single principle, each function does one thing
-
-
-
Inline the function
- Functions can be used directly, without being called indirectly.
-
Inline temporary variables
-
Replaces the action of a variable reference with the expression to which it is assigned
-
If the temporary variable is not declared final, declare it final and compile
-
-
Replace temporary variables with queries
- You should reduce temporary variables and put their expressions in separate functions
-
Introduce explanatory variables
- Complex expressions, you can use temporary variables to grade the expression, with variable names to explain the purpose of the expression
-
Dissect temporary variables
-
Temporary variables should be assigned once. If temporary variables take on too much responsibility, they should be disassembled.
-
Final applies to temporary variables and has only one responsibility
-
-
Removes an assignment to a parameter
-
Assigning a value to an object changes the reference relationship. Reduces code clarity and confuses the way values and addresses are passed
-
Create a temporary variable and assign it the value of the parameter to be processed
-
You can force a parameter to obey non-assignment by adding the keyword final to it. However, final is recommended for long functions
-
If many values are returned, encapsulate them into an object
-
-
Replace functions with function objects
-
If there are too many local variables, it is not easy to extract the function. You can replace them with function objects
-
The commonly used variables and range are extracted as function objects
-
-
Replace your algorithm
-
Replace the algorithm with a cleaner algorithm
-
Test: Call the method before and after the refactoring to see if the return value is consistent
-
-
-
Move properties between objects
-
Move the function
- If a class is used more often, you can extract functions into a class
-
Moving range
- A value field that is used more by a class other than its resident class can be repositioned
-
Derived classes
-
A class should be a clear abstraction that handles clear responsibilities
-
Consider what can be separated out and separated into a separate class
-
For some data in a function that often changes at the same time or even depends on each other, it should be separated
-
-
Inline the class
- Classes that do not take sufficient responsibility should merge functions
-
Hiding the Delegate Relationship
- A class creates all the functions a customer needs to hide delegation
-
Remove the middleman
-
Introducing an external function
- Create a function to call
-
Introduce local outreach
- Add additional functions to extend the function
-
-
Reorganizing data
-
Self – encapsulation value field
- Establishes a value/set function for the field to be encapsulated
-
Objects instead of data values
- Create a class for the value field to encapsulate
-
Change a real value object to a reference object
- Multiple objects can be used as access points for new objects
-
Objects instead of arrays
- The positions of the different elements of an array are difficult to remember and understand
-
Copy monitored data
- Achieve good layering
-
Replace magic numbers with symbolic/literal constants
- The mana value does not properly explain the number
-
Encapsulated domain
-
The value of public was changed to private
-
Provides a value and set function for the public value field
-
-
Replace records with data classes
-
Replace type codes with policies/states
- Using design patterns, each class is a different category
-
-
Reduced conditional expression
-
Decomposition expression
-
If there is a very complex expression in if, it should be distilled into a separate function
-
Forming new branches and functions can highlight conditional logic and make it clearer what each branch does
-
-
Combined expression
-
Merge and recombine conditional fragments
-
Identify code whose execution does not change with conditions
-
If there is more than one code in common, you should consider refining functions
-
-
Movement control marker
- Instead of using the flag to determine whether to end the loop, you can just use break or return
-
Replace nested conditionals with a guard statement
-
A condition is so rare that it should be checked separately and returned from the function as soon as the condition is true
-
The guard statement either returns from a function or is thrown incorrectly
-
-
Replace the conditional expression with polymorphism
- Extract abstract function
-
Introducing null objects
-
Introduction of assertions
-
-
Simplified function calls
-
Renaming functions
- Define the purpose of the function
-
Add parameters
-
Remove parameters
-
Separate the query and modify functions
- Separate query and modify functions
-
Let the function take arguments
- Several functions work similarly, but the functions contain different values that unify the functions
-
Replace arguments with explicit functions
- If different reactions are used within the function entirely depending on the parameter values, a separate function should be created for each possible value
-
Keep objects intact
- If you pass some data in an object as arguments to a function, you can pass the entire object instead
-
Replace arguments with functions
- The result can be obtained using a function
-
Introducing parameter objects
- If some parameters occur at the same time and may be distributed in different classes, they can be extracted into a class
-
Remove Settings object
-
Hiding a function
- A function that is not used by other classes is changed to private
-
Replace constructors with factory functions
-
Encapsulate the downward transition action
- An object returned by a function that requires the function caller to perform a downcast action to move the downcast action to the function
-
Replace error codes with exceptions
-
Replace exceptions with tests
-
-
Dealing with generalizations
-
Range up
- Two with the same range should be moved to the parent class
-
The function up
-
The constructor body moves up
- If a subclass has functions whose ontology code is almost identical, create a new constructor in the parent class and call it in the subclass constructor
-
My function is
-
Domain down
-
Derived subclass
- Create a new subclass and move its features to the subclass for features that are only used by certain entities
-
Refining the parent class
- If two classes have similar features, create a parent class for the two classes and move the same features to the parent class
-
Refined interface
- Refine the same subset of interfaces
-
Folded inheritance relation
- The superclass and subclass are not very different and merge into one
-
Shape template function
- Inheritance is a powerful tool for avoiding repetitive behavior
-
Replace inheritance with delegation
- If a subclass uses only part of the parent’s interface, or does not need to inherit data at all, create a range for the subclass, or create a delegate class, and then remove the inheritance relationship
-
Replace delegation with inheritance
-
-
Large-scale refactoring
-
Sort out and decompose the inheritance system
- One inheritance system has two responsibilities, two inheritance systems are established, and one can call the other through delegation
-
Turn procedural design into object design
- Turn data records into objects, separate behavior, and move behavior into related objects
-
Separate domains from presentation and presentation
-
Refining the inheritance relationship
-