• How to write clean code? Lessons learnt from “The Clean Code” — Robert C. Martin
  • Shubham Gupta
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Prince Stuart
  • Proofreader: PingHGao, NiayyY-S

How to write clean code? Lessons learned from Robert C. Martin’s “The Code Clean Way.

There are two things — programming and good programming. Programming is what we do all the time. Now it’s time to focus on good programming. As we all know, even bad code works. But writing good code takes time and resources. Also, when other developers try to figure out how your code works, they’ll laugh at you. But it’s never too late to take care of your program.

The book gave me a lot of knowledge about best practices and how to code. Now, I’m ashamed of my programming skills. Although I always try to improve my code, this book taught me more.

Now, you’re reading this blog for two reasons. One, you’re a programmer. Second, you want to be a better programmer. Good. We need better programmers.

Characteristics of clean code:

  1. It should be elegant — clean code is a pleasure to read. Reading this code makes you smile, like seeing a well-crafted music box or a well-designed car.
  2. Clean code is focused. Every function, every class, and every module is focused on one thing, completely undistracted by the details around them.
  3. Clean code is intentionally maintained — someone took the time to keep it simple and organized. They paid proper attention to the details. They care.
  4. Can pass all the tests
  5. No duplicate code
  6. Include as few entities as possible, such as classes, methods, functions, etc

How to write clean code?

Meaningful naming

True to its name. Choosing a good name takes time, but you can save more time than you spend. The name of a variable, function, or class should answer all the big questions. It should tell you why it exists, what it does, and how it should be used. If a name needs to be annotated, it’s not worth it.

Eg- int d; // Elapsed time, in days

We should select a name that specifies the object and unit of measurement.

A better name would be: -int elapsedTime. (Although the book says elapsedTimeInDays, I’m still leaning toward the former. Assuming the running time is changed to milliseconds, we have to change int to long and replace elapsedTimeInMillis with elapsedTimeInDays. We don’t know when it will end.

Class Name — The class and object names should be nouns or noun phrases, such as Customer, WikiPage, Account, and AddressParser. Avoid class names like Manager, Processor, Data, or Info. Class names should not be verbs.

Method name – The method name should be a verb or phrasal verb, such as postPayment, deletePage, or save. Attribute accessors, modifiers, and assertions should be named according to their value and prefixed with get and set.

When you overload the constructor, use a static factory method name that describes the parameters. For example,

Complex fulcrumPoint = Complex. FromRealNumber (23.0); Generally better than Complex fulcrumPoint = new Complex(23.0);

There is a word for each concept — pick a word for each abstract concept and stick with it. For example, use fetch, Retrieve, and GET to name the same methods in multiple classes. How can you remember which name corresponds to which class? Similarly, having a controller, a manager, and a driver in a pile of code can be confusing. What is the fundamental difference between DeviceManager and protocol-Controller?

function

The first rule of functions is to be smaller, and the second rule is to be smaller. This means that if statements, else statements, while statements, and so on, should have only one line of code block. This line should probably be a function call statement. Not only does this keep functions short, but it also adds documentation value because the functions called within the block have specific, declarative names.

Function parameters

A function should have no more than 3 arguments; keep them as few as possible. When a function takes more than two or three arguments, those arguments should be wrapped as classes. Reducing the number of parameters by creating parameter objects looks like cheating, but it’s not.

Now, when I say I’m going to reduce the size of the function, you’re probably wondering how to reduce the try-catch content, because it’s making your code more and more bloated. My answer is to simply generate a method that contains only try-catch-finally statements. Separate the try/catch/finally block from the body and form another function.

public void delete(Page page) { 
  try {
     deletePageAndAllReferences(page);
  }
  catch(Exception e) { logError(e); }}private void deletePageAndAllReferences(Page page) throws Exception { 
    deletePage(page);
    registry.deleteReference(page.name); 
    configKeys.deleteKey(page.name.makeKey());
}

private void logError(Exception e) { 
    logger.log(e.getMessage());
}
Copy the code

This makes the logic clear and the function name easier to describe what we want to say. Error handling can be ignored. With this nice partition, the code is much easier to understand and modify.

Error handling is one thing — a function should only do one thing. Error handling is one thing. If the keyword try exists ina function, it should be the first keyword in that function, and nothing else should come after the catch/finally block.

annotation

If you’re writing notes to prove your point, you’re making a big mistake. Ideally, no comments are required at all. If your code needs comments, you’re doing it wrong. Our code should say everything. The modern programming language is English, and it’s much easier to make your point. Proper naming avoids comments.

Except for legal notes, which are necessary. Legal notes refer to copyright and copyright notices.

Objects and data structures

This is a complicated topic, so keep an eye on it. First, we need to clarify the difference between objects and data structures.

Objects hide data behind abstraction, exposing functions that manipulate the data. Data structures expose their data without providing meaningful functions.

These two things are completely different. One is about storing data, and the other is about how to manipulate that data. For example, considering the procedural code shape specification, the Geometry class operates on three shape classes. Shape classes are simple data structures with no behavior. All actions are in the Geometry category.

public class Square { 
  public Point topLeft;
  public double side;
}

public class Rectangle { 
  public Point topLeft; 
  public double height; 
  public double width;
}

public class Circle { 
  public Point center;
  public double radius;
}

public class Geometry {
  public final double PI = 3.141592653589793;
  public double area(Object shape) throws NoSuchShapeException {
    if (shape instanceof Square) { 
        Square s = (Square)shape; 
        return s.side * s.side;
    } else if (shape instanceof Rectangle) {
        Rectangle r = (Rectangle)shape; 
        return r.height * r.width;
    } else if (shape instanceof Circle) {
        Circle c = (Circle)shape;
        return PI * c.radius * c.radius;
    }
        throw newNoSuchShapeException(); }}Copy the code

Imagine adding a perimeter() function to the Geometry class. Those shape classes are not affected at all! On the other hand, if you add a new shape, you have to modify all the functions in Geometry to handle it. Read the code again. Note that these two cases are also directly opposed.

Now consider another approach to the above scenario.

public class Square implements Shape {
  private Point topLeft;
  private double side;
  public double area(a) { 
    returnside*side; }}public class Rectangle implements Shape { 
  private Point topLeft;
  private double height;
  private double width;
  public double area(a) { 
    returnheight * width; }}public class Circle implements Shape {
  private Point center;
  private double radius;
  public final double PI = 3.141592653589793;
  public double area(a) {
    returnPI * radius * radius; }}Copy the code

Now, compared to the previous example, we can easily add a new shape, a data structure. Furthermore, if we only needed to add perimeter() methods to one Shape class, we would have to implement that function in all Shapes classes, which is an interface that contains both area() and Perimeter () methods. This means:

Data structures make it easy to add new functions without changing existing data structures. Object-oriented code (which uses objects) makes it easy to add new classes without changing existing functions.

The reverse also makes sense:

Procedural code (code that uses data structures) is difficult to add new data structures because all functions must be modified. It is difficult to add new functions to object-oriented code because all classes must be modified.

So what’s hard for object-oriented is easy for process-oriented, and what’s easy for object-oriented is hard for process-oriented.

In any complex system, we sometimes want the flexibility to add new data types rather than new functions. In this case, objects and object orientation are most appropriate. Other times, we want the flexibility to add new functions instead of data types. In this case, procedural code and data structures would be more appropriate.

Seasoned programmers know that everything that is an object is just a myth. Sometimes, you really want to do some procedural work on a simple data structure. So you have to think very carefully about what you want to achieve, but also about the prospects for the future, what’s easy to update. In this case, I choose the object-oriented approach because other new shapes may be added later.


I know it’s hard to write good code on a given deadline. But how long will you delay? Start slowly and keep at it. Your code can do wonders for yourself and others (the main beneficiaries). I’ve started and found many of the mistakes I’ve been making. It takes up some extra time each day, but I’ll get paid for it in the future.

This is not the end of the blog. I will continue to write new approaches to code neatness. In addition, I’ll write about some basic design patterns, which developers of any technology must know.

In the meantime, if you enjoy my blog and benefit from it, please clap your hands. It gives me the motivation to create a new blog faster 🙂 comments/suggestions are welcome. Keep learning, keep sharing.

Learn the complete guide to Android APP development

inmindorks.comView all of the top tutorials

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.