How do I get rid of that huge parameter list in the code mountain?

Abstract: Experienced programmers should have seen that a method has dozens or hundreds of parameters.

This article is shared from the HUAWEI CLOUD community " How to eliminate a large list of parameters in the code shit mountain? ", by JavaEdge.

Experienced programmers should have seen that a method has dozens or hundreds of parameters.

1 Why do methods have parameters?

Because information needs to be shared between different methods.

But the way to share information between methods is not only the parameter list, but also global variables. But global variables can always bring unexpected joy, so canceling global variables is also a major language trend. So the parameter list is the only option, so whenever you think of what information to pass to a method, it will be added directly to the parameter list, resulting in a longer and longer parameter list!

2 What happened to the long parameter list?

Once the parameter list is too long, it is difficult for you, a crud boy, to fully control the logic! So the crux is the large number, and the key to solving it is to reduce the number of parameters.

3 Solutions

3.1 Gathering sand into a tower

An easy way to create a blog:

public void createActicle(final String title, 
                          final String introduction,
                          final URL coverUrl,
                          final ActicleType type,
                          final ActicleColumn column,
                          final String protagonists,
                          final String tags,
                          final boolean completed) {
  ...
  Acticle acticle = Acticle.builder
    .title(title) 
    .introduction(introduction)
    .coverUrl(coverUrl)
    .type(type)
    .column(column)
    .protagonists(protagonists)
    .tags(tags)
    .completed(completed)
    .build();
 
  this.repository.save(acticle);
}

The parameter list contains all kinds of information that a blog needs to have, such as: blog title, introduction, cover URL, type, attribution column, protagonist name, label, whether to end...

If you just want to understand the logic, you may also think that this parameter list is very good. The information required to create a blog is passed to the method. This is also the initial point of view for most people to understand the problem when facing a piece of code. While writing code this way is easy to understand, it's not enough for you to spot problems.

Now the product requires to add a piece of information to the blog to identify whether the blog is charged, what should I do? Very simple! I directly add a parameter. A lot of shit mountains come from this way, and the accumulation of small amounts will lead to qualitative changes!

All the parameters here are necessary to create a blog, so what can be done is to encapsulate these parameters into a class, a parameter class for creating a blog:

public class NewActicleParamters {
  private String title;
  private String introduction;
  private URL coverUrl;
  private ActicleType type;
  private ActicleColumn column;
  private String protagonists;
  private String tags;
  private boolean completed;
  ...
}

This leaves the parameter list with only one parameter:

public void createActicle(final NewActicleParamters parameters) {
  ...
}

So, wrap the parameter list into an object  !

Just encapsulate a parameter list into a class, and then when these parameters are used, they need to be taken out one by one. Will this be unnecessary? like this:

public void createActicle(final NewActicleParamters parameters) {
  ...
  Acticle acticle = Acticle.builder
    .title(parameters.getTitle()) 
    .introduction(parameters.getIntroduction())
    .coverUrl(parameters.getCoverUrl())
    .type(parameters.getType())
    .channel(parameters.getChannel())
    .protagonists(parameters.getProtagonists())
    .tags(parameters.getTags())
    .completed(parameters.isCompleted())
    .build();
 
  this.repository.save(acticle);
}

If you think so, it means that you have not yet developed an understanding of software design. We are not simply encapsulating the parameters into classes. From a design perspective, we are introducing a new model.

The encapsulation of a model should be based on [behavior] .

There was no such model before, so I couldn't think of what behavior it should have. Now that the model is generated, it should have its own matching behavior. The behavior of the model is to construct a blog object, and the code can be further refactored:

public class NewActicleParamters {
  private String title;
  private String introduction;
  private URL coverUrl;
  private ActicleType type;
  private ActicleColumn column;
  private String protagonists;
  private String tags;
  private boolean completed;
 
  public Acticle newActicle() {
    return Acticle.builder
      .title(title) 
      .introduction(introduction)
      .coverUrl(coverUrl)
      .type(type)
      .column(column)
      .protagonists(protagonists)
      .tags(tags)
      .completed(completed)
      .build();
  }
}

The way to create a blog is greatly simplified:

public void createActicle(final NewActicleParamters parameters) {
  ...
  Acticle acticle = parameters.newActicle();
 
  this.repository.save(acticle);
}

In this way, once the subsequent requirements are expanded and the content required to create a blog needs to be added, the parameter list is unchanged, that is to say, it is stable!

3.2 Dynamic and static separation

What if this class keeps expanding and becomes a big class? After all, in not all scenarios, parameters belong to a class:

// 根据博客 ID 获取对应章节信息
public void getSections(final long acticleId, 
                        final HttpClient httpClient,
                        final SectionProcessor processor) {
  HttpUriRequest request = createChapterRequest(acticleId);
  HttpResponse response = httpClient.execute(request);
  List<Section> sections = toSections(response);
  processor.process(sections);
}

The number of parameters alone is not much. If you only look at this method, it is difficult to find the immediate problem. The absolute number is not core, and the parameter list should be as small as possible.

But note that the parameters passed in each time:

  • acticleId changes with different requests
  • But the two parameters of httpClient and processor are the same, because they have the same logic, there is no change.
    That is, the change frequency of acticleId is different from the change frequency of httpClient and processor.

Different data change directions are also different concerns. What is shown here is the typical dynamic data (actileId) and static data (httpClient, processor), which are different concerns and should be separated.

For this case:

  • Static immutable data can completely become a field of the class where this method is located
  • Just pass each change as a parameter

Therefore, the code can be refactored as follows:

public void getSections(final long acticleId) {
  HttpUriRequest request = createChapterRequest(acticleId);
  HttpResponse response = this.httpClient.execute(request);
  List<Section> sections = toSections(response);
  this.processor.process(sections);
}

This bad smell is a software design problem. The code lacks the proper structure. Therefore, the part that should belong to the static structure is passed around in the form of dynamic parameters, which invisibly causes the parameter list to become longer.

A long parameter list can certainly be encapsulated by a class, but the premise of being able to encapsulate this class is that these parameters belong to a class and have the same reason for change!

If the parameters of the method have different frequency of change, it depends on the situation. As for the static part, the case in this section has seen that it can become a part of the software structure, and if there are multiple change frequencies, multiple parameter classes can also be encapsulated.

3.3 Bye bye mark!

public void editChapter(final long chapterId, 
                        final String title, 
                        final String content, 
                        final boolean apporved) {
  ...
}

The ID, title and content of the chapter to be modified. The last parameter indicates whether the modification is directly approved.

The first few parameters are necessary information to modify a chapter, and the focus is on the last parameter.

From a business point of view, if the author edits it, it will be reviewed later, and if the editor edits, the review will be passed directly, because the editor itself plays the role of the reviewer. So, as you found out, this parameter is actually a flag that indicates that the next processing flow will be different.

Using marked parameters is a common technique used by programmers when they are beginners in programming. It is this method that is so easy to use, which causes the flag to float freely in the code. Not only are there tags in variables, but also in parameters. Many long parameter lists contain various flag parameters.

In the actual code, the current value of each tag must be carefully judged in order to handle it well.

A simple way to solve the flag parameter is to split the different paths represented by the flag parameter.

A method here can be split into two methods, one method is responsible for "normal editing", and the other is responsible for "editing that can be directly reviewed and approved".

// 普通的编辑,需要审核
public void editChapter(final long chapterId, 
                        final String title, 
                        final String content) {
  ...
}
// 直接审核通过的编辑
public void editChapterWithApproval(final long chapterId,
                                    final String title,
                                    final String content) {
 ...
}

Tag parameters can exist in code in many forms, including booleans, enumerations, strings, or integers. They can all be taken apart by means of the split method. In refactoring, this technique is called Remove Flag Argument.

Only short code can we have better grasp, and to write short code, we need to be able to "separate concerns".

Summarize

The main way to deal with long parameter lists is to reduce the number of parameters. The most direct way is to encapsulate the parameter list into a class. But it does not mean that all cases can be encapsulated into classes to solve, we also need to analyze whether all parameters have the same frequency of change.

If the frequency of change is the same, it is encapsulated into a class.
If the frequency of change is different:

  • Static and invariant, can become a part of the software structure
  • Multiple frequency changes can be encapsulated into several classes

In addition, marked parameters often appear in parameter lists, which is another important reason for long parameter lists. One solution for such flag parameters is to split the method into multiple methods based on these flag parameters.

 

Click Follow to learn about HUAWEI CLOUD's new technologies for the first time~

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324223451&siteId=291194637