Code Refactoring Best Practices: When (and When Not) to Do It

Code Refactoring Best Practices: When (and When Not) to Do It

Today we're going to talk about refactoring. This "smell" arises when a developer adds new functionality to a method: "Why should I put a parameter check into a separate method if I can write the code here?", "Why do I need a separate search method to find the maximum element in an array? Let's keep it here. The code will be clearer this way", and other such misconceptions.

There are two rules for refactoring a long method:

  • If you feel like adding a comment when writing a method, you should put the functionality in a separate method.
  • If a method takes more than 10-15 lines of code, you should identify the tasks and subtasks that it performs and try to put the subtasks into a separate method.

There are a few ways to eliminate a long method:

  • Move part of the method's functionality into a separate method

    If local variables prevent you from moving part of the functionality, you can move the entire object to another method.

  • Using a lot of primitive data types

    This problem typically occurs when the number of fields in a class grows over time. For example, if you store everything (currency, date, phone numbers, etc.) in primitive types or constants instead of small objects.

In this case, a good practice would be to move a logical grouping of fields into a separate class (extract class). You can also add methods to the class to process the data.

  • Too many parameters

This is a fairly common mistake, especially in combination with a long method. Usually, it occurs if a method has too much functionality, or if a method implements multiple algorithms.

Long lists of parameters are very difficult to understand, and using methods with such lists is inconvenient. As a result, it's better to pass an entire object. If an object doesn't have enough data, you should use a more general object or divide up the method's functionality so that each method processes logically related data.

  • Groups of data

Groups of logically related data often appear in code. For example, database connection parameters (URL, username, password, schema name, etc.). If not a single field can be removed from a list of fields, then these fields should be moved to a separate class (extract class).

Solutions that violate OOP principles These "smells" occur when a developer violates proper OOP design. This happens when he or she doesn't fully understand OOP capabilities and fails to fully or properly use them.

Failure to use inheritance If a subclass uses only a small subset of the parent class's functions, then it smells of the wrong hierarchy. When this happens, usually the superfluous methods are simply not overridden or they throw exceptions. One class inheriting another implies that the child class uses nearly all of the parent class's functionality.

Example of a correct hierarchy: How refactoring works in Java - 2

Example of an incorrect hierarchy:How refactoring works in Java - 3

image.png

  • Switch statement

What could be wrong with a switch statement? It is bad when it becomes very complex. A related problem is a large number of nested if statements. Alternative classes with different interfaces Multiple classes do the same thing, but their methods have different names.

  • Temporary field

If a class has a temporary field that an object needs only occasionally when its value is set, and it is empty or, God forbid, null the rest of the time, then the code smells. This is a questionable design decision.

  • Smells that make modification difficult

These smells are more serious. Other smells mainly make it harder to understand code, but these prevent you from modifying it. When you try to introduce any new features, half of the developers quit, and half go crazy.

  • Parallel inheritance hierarchies

This problem is manifests itself when subclassing a class requires you to create another subclass for a different class.

  • Uniformly distributed dependencies

Any modifications require you to look for all of a class's uses (dependencies) and make a lot of small changes. One change — edits in many classes.

  • Complex tree of modifications

This smell is the opposite of the previous one: changes affect a large number of methods in one class. As a rule, such code has cascading dependence: changing one method requires you to fix something in another, and then in the third and so on. One class — many changes.

  • "Garbage smells"

A rather unpleasant category of odors that causes headaches. Useless, unnecessary, old code. Fortunately, modern IDEs and linters have learned to warn of such odors. A large number of comments in a method A method has a lot of explanatory comments on almost every line. This is usually due to a complex algorithm, so it is better to split the code into several smaller methods and give them explanatory names.

  • Duplicated code

Different classes or methods use the same blocks of code.

  • Lazy class A class takes on very little functionality, though it was planned to be large.
  • Unused code A class, method or variable is not used in the code and is dead weight.
  • Excessive connectivity This category of odors is characterized by a large number of unjustified relationships in the code.
  • External methods A method uses data from another object much more often than its own data.
  • Inappropriate intimacy A class depends on the implementation details of another class.
  • Long class calls One class calls another, which requests data from a third, which get data from a fourth, and so on. Such a long chain of calls means high dependence on the current class structure.
  • Task-dealer class A class is needed only for sending a task to another class. Maybe it should be removed? Refactoring techniques Below we'll discuss basic refactoring techniques that can help eliminate the described code smells.