Write quality code that others can understand

Write quality code that others can understand

[[150631]]

With the continuous development of the software industry, there are more and more legacy programs, and the maintenance cost of the code is getting higher and higher, even higher than the development cost. The development of new features often relies on old code, and the time spent reading old code is almost longer than writing new code.

I read a book a few days ago, and there was a sentence in the book:

“Complex code is often written by novices. Only experienced experts can write simple, expressive code.”

Although this statement is a bit exaggerated, it also illustrates the importance of experience.

The code we write needs to be read by others in addition to being executed by machines. So we have to write:

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

Points 2 and 3 emphasize object-oriented design principles. This article focuses more on local code issues. By giving examples, this article summarizes common mistakes and optimization methods.

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

1. DRY (Don't repeat yourself)

This principle is so important because:

  • Less code means fewer bugs
  • Code without duplicate logic is easier to maintain. When you fix a bug, if the same logic appears in another place without you realizing it, do you feel wronged?

2. TED Principles

  • Terse
  • Expressive
  • Do one thing

3. Examples

1. Avoid comments and use code to illustrate comments

Counterexample:

  1. /// <summary>  
  2. /// !@#$%^&^&*((!@#$%^&^&*((!@#$%^&^&*((!@#$%^&^&*((  
  3. /// </summary>  
  4. /// <returns></returns>  
  5. public decimal GetCash()
  6. {
  7. //!@#$%^&^&*((!@#$%^&^&*((  
  8. var a = new List<decimal>() { 2m, 3m, 10m };
  9. var b = 2 ;
  10. var c = 0m;
  11. //!@#$%^&^&*((!@#$%^&^&*((!@#$%^&^&*((  
  12. foreach (var p in a)
  13. {
  14. c += p*b;
  15. }
  16. return c;
  17. }

After refactoring:

  1. public decimal CalculateTotalCash()
  2. {
  3. var prices= new List<decimal>(){2m,3m,10m};
  4. var itemCount = 2 ;
  5. return prices.Sum(p => p*itemCount);
  6. }

Good code naming can completely replace the role of comments. If you are trying to write a comment, from a certain perspective, you are trying to write a piece of code that others cannot understand.

When you cannot come up with an accurate name for your method, it is likely that your method does more than one thing and violates the Do one thing rule. Especially when you want to add words such as And, Or, If to the method name.

2. Assigning values ​​to Boolean variables

Counterexample:

  1. public bool IsAdult( int age)
  2. {
  3. bool isAdult;
  4. if (age > 18 )
  5. {
  6. isAdult = true ;
  7. }
  8. else  
  9. {
  10. isAdult = false ;
  11. }
  12. return isAdult;
  13. }

After refactoring:

  1. public bool IsAdult( int age)
  2. {
  3. var isAdult = age > 18 ;
  4. return isAdult;
  5. }

3. Double Negation Conditional Judgment

Counterexample:

  1. if (!isNotRemeberMe)
  2. {
  3.   
  4. }

After refactoring:

  1. if (isRemeberMe)
  2. {
  3.   
  4. }

Whether you have seen such conditions or not, I have. When I saw such conditional judgments, I was immediately dizzy.

4. Refuse HardCode and Dig a Hole

Counterexample:

  1. if (carName == "Nissan" )
  2. {
  3.   
  4. }

After refactoring:

  1. if (car == Car.Nissan)
  2. {
  3.   
  4. }

Since we are using a strongly typed language, we can use the compiler's functionality to make errors occur during the compilation phase.

5. Reject magic numbers and avoid traps

Counterexample:

  1. if (age > 18 )
  2. {
  3.   
  4. }

After refactoring:

  1. const   int adultAge = 18 ;
  2. if (age > adultAge)
  3. {
  4.   
  5. }

The so-called magic number is a magic number. The reader has no idea what your number is. This kind of code is often seen.

6. Complex conditional judgment

Counterexample:

  1. if (job.JobState == JobState.New
  2. || job.JobState == JobState.Submitted
  3. || job.JobState == JobState.Expired
  4. || job.JobTitle.IsNullOrWhiteSpace())
  5. {
  6. //....  
  7. }

After refactoring:

  1.      if (CanBeDeleted(job))
  2. {
  3. //  
  4. }
  5.   
  6. private bool CanBeDeleted(Job job)
  7. {
  8. var invalidJobState = job.JobState == JobState.New
  9. || job.JobState == JobState.Submitted
  10. || job.JobState == JobState.Expired;
  11. var invalidJob = string.IsNullOrEmpty(job.JobTitle);
  12.   
  13. return invalidJobState || invalidJob;
  14. }

Do you feel a sudden sense of enlightenment?

7. Nested judgment

Counterexample:

  1. var isValid = false ;
  2. if (!string.IsNullOrEmpty(user.UserName))
  3. {
  4. if (!string.IsNullOrEmpty(user.Password))
  5. {
  6. if (!string.IsNullOrEmpty(user.Email))
  7. {
  8. isValid = true ;
  9. }
  10. }
  11. }
  12. return isValid;

After refactoring:

  1. if (string.IsNullOrEmpty(user.UserName)) return   false ;
  2. if (string.IsNullOrEmpty(user.Password)) return   false ;
  3. if (string.IsNullOrEmpty(user.Email)) return   false ;
  4. return   true ;

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

8. Use preconditions

Counterexample:

  1. if (!string.IsNullOrEmpty(userName))
  2. {
  3. if (!string.IsNullOrEmpty(password))
  4. {
  5. //register  
  6. }
  7. else  
  8. {
  9. throw   new ArgumentException( "user password can not be empty" );
  10. }
  11. }
  12. else  
  13. {
  14. throw   new ArgumentException( "user name can not be empty" );
  15. }

After refactoring:

  1. if (string.IsNullOrEmpty(userName)) throw   new ArgumentException( "user name can not be empty" );
  2. if (string.IsNullOrEmpty(password)) throw   new ArgumentException( "user password can not be empty" );
  3. //register  

The refactored style is closer to contract programming. The prerequisites must be met first, otherwise there is no point in talking about it.

9. Too many parameters, more than 3

Counterexample:

  1. public   void RegisterUser(string userName, string password, string email, string phone)
  2. {
  3.   
  4. }

After refactoring:

  1. public   void RegisterUser(User user)
  2. {
  3.   
  4. }

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

10. Method signature contains Boolean parameters

Counterexample:

  1. public   void RegisterUser(User user, bool sendEmail)
  2. {
  3.   
  4. }

After refactoring:

  1. public   void RegisterUser(User user)
  2. {
  3.   
  4. }
  5.   
  6. public   void SendEmail(User user)
  7. {
  8.   
  9. }

Boolean parameters tell the method to do more than one thing, violating the Do one thing

10. Write expressive code

Counterexample:

  1. private string CombineTechnicalBookNameOfAuthor(List<Book> books, string author)
  2. {
  3. var filterBooks = new List<Book>();
  4.   
  5. foreach (var book in books)
  6. {
  7. if (book.Category == BookCategory.Technical && book.Author == author)
  8. {
  9. filterBooks.Add(book);
  10. }
  11. }
  12. var name = "" ;
  13. foreach (var book in filterBooks)
  14. {
  15. name += book.Name + "|" ;
  16. }
  17. return name;
  18. }

After refactoring:

  1. private string CombineTechnicalBookNameOfAuthor(List<Book> books, string author)
  2. {
  3. var combinedName = books.Where(b => b.Category == BookCategory.Technical)
  4. .Where(b => b.Author == author)
  5. .Select(b => b.Name)
  6. .Aggregate((a, b) => a + "|" + b);
  7.   
  8. return combinedName;
  9. }

Compared with imperative code, declarative code is more expressive and concise, which is one of the reasons why functional programming is becoming more and more popular.

4. About DRY

When we refactor code, an important idea is DRY. I want to share a counterexample of DRY:

There will be various MODEL layers in the project architecture, such as DomainModel, ViewModel, DTO. In many cases, most of the fields in these models are the same, so some people will think of the DRY principle and simply use one type to save copying and pasting and converting back and forth.

The fundamental reason why this counterexample fails is that these models have different responsibilities. Although the content is repeated in most cases, they play different roles.

Consider this scenario: DomainModel has a field DateTime Birthday{get;set;}, and ViewModel also has DateTime Birthday{get;set;}. Requirements upgrade: The interface no longer displays birthdays, but only whether an adult is required. We only need to add a Bool IsAdult{get{return ....}} to the ViewModel, and the DomainModel does not need to change at all.

5. Use advanced production tools

Taking the Reshaper in the VS plugin as an example, Reshaprer can give different degrees of hints for most of the counterexamples listed in this article. After a period of practice, when Reshaper can't give any hints for your code, your code will be significantly improved.

Screenshot illustrating Reshaper's prompt function:

Move the cursor to the wavy line, then press Alt+Enter, Resharper will automatically optimize the code

If you can avoid the counterexamples summarized in this article, your code already has the genes of excellent code. Of course, high-quality code also requires good design and adherence to the principles of object-oriented programming. If you want to learn more about this, please read "Code Complete", "Clean Code", "Refactoring to Improve the Design of Existing Code", "Agile Software Development Principles, Patterns and Practices"

<<:  Animation Interaction Learning Guide Inspired by Google Motion Design

>>:  The market will be bigger. Mark Zuckerberg called at the United Nations to allow more people around the world to access the Internet.

Recommend

How to plan the core selling points of a brand? One model to get it done

The company's new brand is about to enter the...

Review: Thoughts on APP listing on overseas app stores and localization

The author joined an online literature export pro...

MoKee OpenSource 51.1 was officially released on August 1

[[143398]] MoKee OpenSource 51.1 2015-08-01 Offic...

Introduction to HTTPS encryption methods and their advantages and disadvantages

Symmetric encryption Symmetric encryption refers ...

Short video operation matrix gameplay!

Account matrix has always been the most common op...

10 products that died in 2014: XP, iPod Classic, and more

Every year, the technology industry brings unexpe...

YouTube video marketing, tips to improve ad conversion rates!

Are you struggling to grow your YouTube channel? ...

2019 Second Category E-commerce Promotion Insight Report!

The traditional e-commerce dividend has passed. I...

The video has no recommendations? Mostly these are the questions!

Friends who make videos must understand that if t...

Blind box marketing, is it no longer working?

The blind box circle doesn’t seem to be peaceful ...

Enterprise Douyin live streaming sales plan!

1. Industry Background In 2019, many people in th...

Short video fan increase skills: "topic planning"

The sudden popularity of TikTok has promoted the ...