Refactoring and Code Smells

Refactoring:

Code refactoring is the process of restructuring existing computer code—changing its structure—without changing its external behavior. The goal of refactoring is to improve the design, structure, and/or implementation of the software (its non-functional attributes), while preserving its functionality.

 

Refactoring can be driven by different needs:

  1. Improving Code Readability and Reducing Complexity: Overly complex code can be hard to understand and maintain. Refactoring can simplify this complexity and make the code more readable, and therefore more maintainable.

  2. Removing Duplicate Code: Duplication can make the system harder to maintain because if a change is required, it needs to be done in multiple places.

  3. Improving Performance: While the primary goal of refactoring is improving the design of the software, it can also have secondary benefits like improving performance.

  4. Identifying and Preparing for Future Changes: When making a change to the code, developers often need to understand the code first. Refactoring can improve their understanding of it and thus help them make the change more effectively.

  5. Improving Software Architecture: Over time, software architecture can degrade (a process known as "software rot" or "code rot"). Regular refactoring can help maintain a high-quality software architecture.

Refactoring can involve several techniques, such as:

  • Renaming variables, methods, and classes to improve readability and better reflect their purpose.

  • Extracting code into methods or classes to reduce complexity and avoid duplication. This can involve creating a new method (Extract Method) or a new class (Extract Class).

  • Inline refactoring, which is the opposite of extraction. If a method's body is as clear as its name, or if a class isn't doing enough to earn your attention, you use Inline Method or Inline Class.

  • Replacing conditional with polymorphism, when you have a conditional that chooses different behavior depending on the type of an object.

  • Replacing magic numbers with named constants, to improve readability.

It's important to note that refactoring should be done in small steps and should be tested after each step to ensure that functionality remains unchanged. This can be done effectively with a good suite of unit tests.

Martin Fowler's book, "Refactoring: Improving the Design of Existing Code", is considered the foundational work on this topic.

Code Smells

"Code smells" is a term coined by Kent Beck in the book "Refactoring: Improving the Design of Existing Code" by Martin Fowler. A code smell is a surface indication that usually corresponds to deeper problems in a system. They're not bugs or errors; rather, they're characteristics in the source code that suggest poor design or poor coding practices which may slow down development, increase the risk of bugs, or complicate modifications or extensions of the system.

Here are some examples of common code smells:

  1. Large Class / Long Method: Classes or methods that have grown too large can be hard to understand and maintain. It's usually better to break them up into smaller, more focused classes or methods.

  2. Duplicated Code: If the same code structure is found in more than one place, it's generally a good idea to consolidate it into one place. This can be done by extracting the duplicate code into a separate method or by using a design pattern that allows for code reuse.

  3. Feature Envy: This happens when a method accesses the data of another object more than its own data. It could be a sign that the method is in the wrong class.

  4. Long Parameter List: Methods that require a lot of parameters can be difficult to understand and use. Possible solutions include passing a single object instead of several parameters, or using method chaining or parameter object.

  5. Data Clumps: Sometimes different pieces of the code seem to always go together. It would probably be more advantageous to group them together in a class.

  6. Switch Statements: They often indicate that a design pattern, like polymorphism, could be used to make the code easier to read and maintain.

  7. Lazy Class / Dead Code: Classes or methods that don't do enough or aren't used can often be eliminated.

  8. Speculative Generality: This happens when developers create classes, methods, or other constructs to be used in the future, but they're not needed for the current project. It's usually best to keep the code as simple as possible and only add functionality when it's needed.

  9. Temporary Field: This smell occurs when an object's class has a field that contains data only some of the time.

  10. Refused Bequest: This is when a subclass only uses a portion of the properties and methods of its superclass. The hierarchy might need to be reevaluated in this case.

These code smells suggest that refactoring may be needed to improve the structure and design of the code, making it more understandable and maintainable. It's important to remember, however, that not every instance of a code smell necessarily needs to be refactored; they are more guidelines than strict rules.

COSC-1437 / ITSE-2457 Computer Science Dept. - Author: Dr. Kevin Roark