Abstract: With the continuous development of software industry, there are more and more programs left over from history, and the maintenance cost of code is getting bigger and bigger, even greater than the development cost. The development of new features often relies on old code, and the time spent reading old code is almost greater than the time spent writing new features.

I read a book a few days ago, there is such a sentence: “Complex code is often written by beginners, only experienced masters can write simple and expressive code.” Although this sentence is a bit exaggerated, it also shows the importance of experience and wisdom.

The code we write is written primarily to be read, and second to be executed by machines. So we’ll write:

  1. Code that others can understand
  2. Extensible code
  3. Testable code (Code should be testable, and writing tests on code that is not testable is a waste of life)

The two and three points are more emphasis on object-oriented design principles. In this paper, more attention is paid to local code problems, this paper summarizes the usual mistakes and optimization methods through examples.

The examples in this article are based on two guiding principles:

A. DRY (Don ‘t repeat yourself)

This principle is important simply because:

  • The less code there is, the less bugs there are
  • Code without repetitive logic is easier to maintain. When you fix a bug, do you feel guilty if the same logic appears in another place without you realizing it?

2. TED principle

  • Concise, Terse
  • Expressive (Expressive)
  • Do one thing

Three. Give examples

1. Reject comments and explain them in code

Example:

/// / / /! @ # $% ^ & ^ & * ((! @ # $% ^ & ^ & * ((! @ # $% ^ & ^ & * ((! @ # $% ^ & ^ & * ((/ / //// public decimal GetCash() { //! @ # $% ^ & ^ & * ((! @#$%^&^&*(( var a = new List() { 2, 3, 10 }; var b = 2m; var c = 0m; / /! @ # $% ^ & ^ & * ((! @ # $% ^ & ^ & * ((! @#$%^&^&*(( foreach (var p in a) { c += p*b; } return c; }Copy the code

After the refactoring:

Public decimal CalculateTotalCash() {var itemCounts=new List(){2,3,10}; var price = 2m; return itemCounts.Sum(p => p*price ); }Copy the code

Good code naming is a complete substitute for comments, and if you’re trying to write a comment, you’re trying to write code that no one else can understand, at some point.

When you can’t come up with an exact name for your method, chances are your method does more than one thing. Especially If you want to include words like And, Or, Or If in the method name

2. Assign values to Boolean variables

Example:

public bool IsAdult(int age)
{
    bool isAdult;
    if (age > 18)
    {
        isAdult = true;
    }
    else
    {
        isAdult = false;
    }
    return isAdult;
}

Copy the code

After the refactoring:

public bool IsAdult(int age)
{
    var isAdult = age > 18;
    return isAdult;
}
Copy the code

3. Double negative conditional judgment

Example:

if (! isNotRemeberMe) { }Copy the code

After the refactoring:

if (isRemeberMe)
{
 
}
Copy the code

Whether you’ve seen conditions like this or not, I’ve seen them. Seeing such a conditional judgment, I was immediately dizzy.

4. Refuse HardCode, refuse to dig holes

Example:

if (carName == "Nissan")
 {
 
}

Copy the code

After the refactoring:

if (car == Car.Nissan)
{
 Copy the code

Since we’re playing a strongly typed language, let’s use the compiler’s ability to make errors happen at compile time

5. Refuse magic numbers, refuse to dig holes

Example:

if (age > 18)
{
 
}

Copy the code

After the refactoring:

const int adultAge = 18;
 if (age > adultAge)
{
 
}Copy the code

The so-called Magic number (Magic number) is a Magic number, readers can not understand what your number is, such code usually see more

6. Complex conditional judgments

Example:

if (job.JobState == JobState.New
    || job.JobState == JobState.Submitted
    || job.JobState == JobState.Expired
    || job.JobTitle.IsNullOrWhiteSpace())
{
    //....
}

Copy the code

After the refactoring:

if (CanBeDeleted(job)) { // } private bool CanBeDeleted(Job job) { var invalidJobState = job.JobState == JobState.New ||  job.JobState == JobState.Submitted || job.JobState == JobState.Expired; var invalidJob = string.IsNullOrEmpty(job.JobTitle); return invalidJobState || invalidJob; }Copy the code

Is there a sudden leap of faith?

7. Nested judgments

Example:

var isValid = false; if (! string.IsNullOrEmpty(user.UserName)) { if (! string.IsNullOrEmpty(user.Password)) { if (! string.IsNullOrEmpty(user.Email)) { isValid = true; } } } return isValid;Copy the code

After the refactoring:

if (string.IsNullOrEmpty(user.UserName)) return false;
if (string.IsNullOrEmpty(user.Password)) return false;
if (string.IsNullOrEmpty(user.Email)) return false;
 return true;
Copy the code

The first code is inspired by some of the earlier ideas: use a variable to store the return result. It turns out that you should return as soon as you know the results.

8. Use preconditions

Example:

if (! string.IsNullOrEmpty(userName)) { if (! string.IsNullOrEmpty(password)) { //register } else { throw new ArgumentException("user password can not be empty"); } } else { throw new ArgumentException("user name can not be empty"); }Copy the code

After the refactoring:

if (string.IsNullOrEmpty(userName)) throw new ArgumentException("user name can not be empty");
if (string.IsNullOrEmpty(password)) throw new ArgumentException("user password can not be empty");
//register
Copy the code

The refactoring style is closer to contract programming, where the preconditions are met or not.

9. The number of parameters exceeds 3

Example:

public void RegisterUser(string userName, string password, string email, string phone)
{
 
}

Copy the code

After the refactoring:

public void RegisterUser(User user)
{
 
}
Copy the code

Too many parameters make it difficult for the reader to grasp the intent of the code, and too many parameters can affect the stability of the method. It also indicates that the parameters should be aggregated into a Model

10. The method signature contains a Boolean parameter

Example:

public void RegisterUser(User user, bool sendEmail)
 {
 
 }
Copy the code

After the refactoring:

public void RegisterUser(User user)
{
 
}
 
public void SendEmail(User user)
{
 
}Copy the code

Boolean arguments tell methods to Do more than one thing, violating Do one thing

11. Write expressive code

Example:

private string CombineTechnicalBookNameOfAuthor(List books, string author)
{
    var filterBooks = new List();
 
    foreach (var book in books)
    {
        if (book.Category == BookCategory.Technical && book.Author == author)
        {
            filterBooks.Add(book);
        }
    }
    var name = "";
    foreach (var book in filterBooks)
    {
        name += book.Name + "|";
    }
    return name;
}
Copy the code

After the refactoring:

private string CombineTechnicalBookNameOfAuthor(List books, string author)
 {
     var combinedName = books.Where(b => b.Category == BookCategory.Technical)
         .Where(b => b.Author == author)
         .Select(b => b.Name)
         .Aggregate((a, b) => a + "|" + b);
 
     return combinedName;
 }Copy the code

Declarative code is more expressive and concise than imperative code. This is one of the reasons why functional programming is gaining popularity.

4. DRY

When you refactor code, an important idea is DRY. I would like to share a DRY counter example:

The project will have various MODEL layers during the architecture process, such as: DomainModel, ViewModel, DTO. Most of the time, the fields in these models are the same, so one might think of the DRY principle and simply use one type instead of pasting and copying and converting.

The fundamental reason for the failure of this counterexample is that each of these models has different responsibilities and, although in most cases they overlap, they play different roles.

Consider this scenario: DomainModel has a field DateTime Birthday{get; set; }, ViewModel also has DateTime Birthday{get; set; }. Requirement upgrade: request interface no longer display birthday, only need to display whether adult. We just need to add a Bool IsAdult{get{return…. to the ViewModel }} DomainModel does not change at all.

Make use of advanced production tools

Most of the examples here are from Reshaper, the VS plug-in, which gives different levels of cues. After a while of practice, your code will improve dramatically when Reshaper doesn’t give you any cues.

The prompt functions of Reshaper:

Move the cursor over the wavy line, then Alt+Enter, and Resharper will automatically optimize the code

If you can avoid the counterexamples summarized in this article, your code already has the genes for good code. Of course, high-quality code also requires good design and adherence to object-oriented programming principles. For more on this, see Code Code, Clean Code, Refactoring to Improve the Design of Existing Code, and Agile Software Development Principles, Patterns, and Practices.

Addendum: Since you don’t think it’s feasible to reject comments, LET me elaborate: 1. If your team’s code is well named, well written, and doesn’t have some weird implementation, try uncommenting it. Because of the cost of writing comments and the fact that they are often ignored when refactoring code, comments become a liability because they are not accurate over time. 2. If your team code is poorly written and poorly named, comments are necessary, and inaccurate comments are better than reading code.