This article is a revised version. It was previously published atย refactoring-legacy-code.netย published.
This is a series of articles. You can find the other articles here:
- Simple refactorings - Part 1
- Simple refactorings - Part 3
- Simple refactorings - Part 4
- Simple refactorings - Part 5
Raise the level of abstraction - Extract Class from Parameters
The Extract Class from Parameters Refactoring helps if the parameter list of a method has become a little too long over the years. Sometimes the parameters are related to each other in a meaningful way so that they can be combined into one class. This is not always the case, especially if a method is responsible for more than one aspect. In such cases, individual parameters or groups of parameters are often required for the different aspects of the method. In this case, it can help to separate the aspects first. More on this below.
If the parameters of a method are related in terms of content, it is sometimes useful to combine them into a separate type. This makes the signature of the method easier to understand because the type name conveys the meaning of the individual parameters better. The reader does not have to mentally summarize the parameters themselves each time, but can recognize this immediately from the selected type name. This results in a further level of abstraction. The following example shows a method with many parameters:
public string Format(string firstname, string lastname, string street, string hn, string plz, string city) { return $"{firstname} {lastname}, {street} {hn}, {plz} {place}"; }
Readability can be improved here by using Extract Class from Parameters refactoring:
public string Format(address address, string firstname, string lastname) { return $"{firstname} {lastname}, {address.street} {address.hn}, {address.zip} {address.city}"; }
In this example, I have set the parameters first name and Surname as independent parameters quite arbitrarily. The way in which you combine the parameters into a class depends on the specific context. A summary of all original parameters would also have been conceivable here. In the example, ReSharper has given me the following class Address generated:
public class Address { private string strasse; private string hn; private string plz; private string ort; public address(string strasse, string hn, string plz, string ort) { this.strasse = strasse; this.hn = hn; this.plz = plz; this.ort = ort; } public string street { get { return strasse; } } public string Hn { get { return hn; } } public string Plz { get { return plz; } } public string City { get { return ort; } } }
Conclusion
Use Extract Class from Parametersin order to Signature a method easier to read to make. The additional identifier results in a Higher level of abstraction.
Separate aspects - Extract Method
A method often consists of blocks of related lines of code, each of which performs a subtask of the overall problem. Sometimes the code blocks are also provided with comments, as in the following example:
public IDictionary ToDictionary(string configuration) { // Split configuration into settings var settings = configuration.Split(';'); // Split settings into key/value pairs var pairs = new List<KeyValuePair>(); foreach (var setting in settings) { var keyAndValue = setting.Split('='); pairs.Add(new KeyValuePair(keyAndValue[0], keyAndValue[1])); } // Insert pairs into dictionary var result = new Dictionary(); foreach (var pair in pairs) { result.Add(pair.Key, pair.Value); } return result; }
In such cases, readability can be significantly improved by outsourcing the individual code blocks to methods. To do this, select several lines and then start the Extract Method Refactoring your IDE. Repeat the process as often as possible until the original method only contains method calls. The end result then looks like this:
public IDictionary ToDictionary(string configuration) { var settings = SplitIntoSettings(configuration); var pairs = SplitIntoPairs(settings); var result = InsertIntoDictionary(pairs); return result; }
In this example, multiple applications of Extract Method a clear separation of integration and operation can be achieved. The resulting method ToDictionary integrates the methods that are achieved by applying Extract Method have been created. The principle Single Level of Abstraction (SLA) which states that a method should only have one level of abstraction. This applied to the original method, which was at a low level of abstraction. In the version after refactoring, the SLA principle is also adhered to. The code of the method is completely at a higher level of abstraction. If, after the first Extract Method would have stopped, different levels of abstraction would have remained in the method.
The separation of Integration and Operation creates a level of abstraction that did not exist before. The entire solution to the problem ToDictionary was originally at a low level of abstraction in the method. The method was an operation at a low level of abstraction. In order to give the reader an indication of the meaning of the code blocks, they were each provided with comments. By repeated Extract Method is the code of the ToDictionary method is now more abstract than before. The solution is easier for the reader to grasp. In addition, a reader can now decide whether they are only interested in the abstract or also in the details. This is the main reason why the version after the refactoring differs significantly from the original version. There, the reader did not have a choice but was always confronted with the entire code.
Extracting the methods also meant that the aspects that make up the solution to the problem ToDictionary are no longer mixed but separate.
It is not always possible to split the code of a method as easily as shown in the example. Especially when code areas are nested in loops or conditions, extracting and completely separating the aspects is no longer so easy. Nevertheless Extract Method This also increases the readability of the code because it introduces a further level of abstraction. The following code snippet shows a loop in which a Setting is processed. The content of the loop is very short at two lines, but it is still worth using a Extract Method Refactoring:
foreach (var setting in settings) { var key_and_value = setting.Split('='); result.Add(key_and_value[0], key_and_value[1]); }
After refactoring, the loop looks like this:
foreach (var setting in settings) { AddSettingToResult(setting, result); }
As a reader, I do not have to read the entire content of the loop line by line, but only have to deal with a method call. If I am interested in the details of the method, I have the option of looking into the extracted method. This results in another level of abstraction - as a reader, I can choose whether I want to delve deeper into the details or not. If the body of the loop is complete with all details within the loop, I have no choice but to look at the loop body line by line.
Conclusion
Use Extract Methodto Lines of code into a method to outsource. This gives you the opportunity to give the code area a Identifier and thus the Meaning to communicate. The comments that may originally have been present are often rendered superfluous by the introduction of the method. Furthermore, by introducing additional methods, you enable the reader to analyze the solution level on a higher level of abstraction to read. Furthermore, the Separate aspectsso that the original method is only responsible for integrating the individual aspects after refactoring. Through Extract Method can often apply the principle Single Level of Abstraction can be achieved.