Reading notes on the way of code neatness

Keywords: xml less Google C

Why keep your code clean?

Tall:

  1. Software quality is not only dependent on architecture and project management, but also closely related to code quality, which is directly proportional to its cleanliness.
    Software quality < = > code quality < = > cleanliness.
  2. If software is compared to a grand building, the smallest part of the grand building, such as the door that can't be closed tightly, the floor that hasn't been paved a little, even the messy desktop, will destroy the charm of the whole situation. That's what neat code is about.

Practical fit:

  1. The cost of code chaos: as chaos increases, team productivity continues to decline, toward zero. (chaos increases, productivity declines - > management increases staff, expecting to improve productivity - > new people are not familiar with the design of the system, creating more chaos).
  2. Examples of bad code destroying applications
    In the late 1980s, a company wrote a popular killer app that many professionals bought. ->The release cycle starts to len gt hen, the defects can't be fixed, the loading time is longer and longer, and the probability of crash is higher and higher. ->The client shut down the program in frustration and never used it again.
    reason:
    • rushing to launch the product, the code is in a mess.
    • the more features you have, the worse your code gets, and finally you can't manage it anymore.
    • bad code ruined the company.

Why write bad code?

  1. Everyone has been bothered by bad code, so why write bad code?
    Pursue efficiency and want to finish it quickly. (wife and children go home to heat the Kang)
    • in a hurry. Maybe you don't think you have enough time to do a good job. If you take the time to clean up the code, the boss will be furious
    • maybe you're just impatient to go through the process again, hoping to end it sooner.
    • maybe you look at the other things you promised to do, and realize you need to finish what you have in hand, so that you can move on to the next job.
  2. Didn't you clean up the mess caused by your own hands in time?
    It's better to see that your bad program can run, and then assert that the bad program can run is better than nothing. ->We've all said that one day we'll go back and clean up. ->LeBlanc's Law: Later equals never.

What is clean code?
• easy to read
• no duplicate code
• minimal dependency
• each function, class, module does only one thing.

Chapter 2 meaningful naming

1. Worthy of the name

The name of a variable, function, or class should have answered all the big questions. It should tell you why it exists, what it does, and how it should be used. If the name needs to be annotated, it's not true.

int d;   // Lost time, in days, d nothing
// The name indicating the object and unit of measurement shall be selected
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for(int[] x : theList) {
        if(x[0] == 4) {
            list1.add(x);
        }
    }
    return list1;
}

• what kind of things are in the list?
• what is the meaning of the list subzero entry?
• what is the meaning of a value of 4?
• how do I use the returned list?

public List<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<int[]>();
    for(int[] cell : gameBoard) {
        if(cell[STATUS_VALUE] == FLAGGED) {
            flaggedCells.add(cell);
        }
    }
    return flaggedCells;
}

2. Make a meaningful distinction

getActiveAccount();
getActiveAccounts();
getActiveAccountInfo();
I don't know which function to call at all.

3. Use searchable names

Find MAX_CLASSES_PER_SUTUDENT is easy, but it's hard to find the number 7.

4. Class name and object name should be noun or noun phrase, method name should be verb or verb phrase

Chapter 3 functions

1. Short

The first rule of a function is to be shorter, and the second rule is to be shorter. (according to the group code specification, the number of function body lines should be less than 80)

public static String testableHtml(
     PageData pageData,
     boolean includeSuiteSetup
   ) throws Exception {
     WikiPage wikiPage = pageData.getWikiPage();
     StringBuffer buffer = new StringBuffer();
     if (pageData.hasAttribute("Test")) {
       if (includeSuiteSetup) {
         WikiPage suiteSetup =
           PageCrawlerImpl.getInheritedPage(
               SuiteResponder.SUITE_SETUP_NAME, wikiPage
           );
         if (suiteSetup != null) {
           WikiPagePath pagePath =
             suiteSetup.getPageCrawler().getFullPath(suiteSetup);
           String pagePathName = PathParser.render(pagePath);
           buffer.append("!include -setup .")
                 .append(pagePathName)
                 .append("\n");
         }
       }
       WikiPage setup =
         PageCrawlerImpl.getInheritedPage("SetUp", wikiPage);
       if (setup != null) {
         WikiPagePath setupPath =
           wikiPage.getPageCrawler().getFullPath(setup);
         String setupPathName = PathParser.render(setupPath);
         buffer.append("!include -setup .")
               .append(setupPathName)
               .append("\n");
       }
     }
     buffer.append(pageData.getContent());
     if (pageData.hasAttribute("Test")) {
       WikiPage teardown =
         PageCrawlerImpl.getInheritedPage("TearDown", wikiPage);
       if (teardown != null) {
         WikiPagePath tearDownPath =
           wikiPage.getPageCrawler().getFullPath(teardown);
         String tearDownPathName = PathParser.render(tearDownPath);
         buffer.append("\n")
               .append("!include -teardown .")
               .append(tearDownPathName)
               .append("\n");
       }
       if (includeSuiteSetup) {
         WikiPage suiteTeardown =
           PageCrawlerImpl.getInheritedPage(
                   SuiteResponder.SUITE_TEARDOWN_NAME,
                   wikiPage
           );
         if (suiteTeardown != null) {
           WikiPagePath pagePath =
             suiteTeardown.getPageCrawler().getFullPath (suiteTeardown);
           String pagePathName = PathParser.render(pagePath);
           buffer.append("!include -teardown .")
                 .append(pagePathName)
                 .append("\n");
         }
      }
    }
    pageData.setContent(buffer.toString());
    return pageData.getHtml();
   }

Do you understand the whole function? Probably not. There are too many things happening, too many different levels of abstraction, strange strings and function calls, if statements controlled by identifiers, etc.
However, with just a few simple methods to extract and rename, and a little refactoring, it can be done in 9 lines of code.

public static String renderPageWithSetupsAndTeardowns(
    PageData pageData, boolean isSuite) throws Exception {
    boolean isTestPage = pageData.hasAttribute("Test");
    if(isTestPage) {
        WikiPage testPage = pageData.getWikiPage();
        StringBuffer newPageContent = new StringBuffer();
        includeSetupPages(testPage, newPageContent, isSuite);
        newPageContent.append(pageData.getContent());
        includeTeardownPages(testPage, newPageContent, isSuite);
        pageData.setContent(newPageContent.toString());
    }
    return pageData.getHtml();
}

It can also be shorter:

public static String renderPageWithSetupsAndTeardowns(
    PageData pageData, boolean isSuite) throws Exception {
    if(isTestPage(pageData)) {
        includeSetupAndTeardownPages(pageData, isSuite);
    }
    return pageData.getHtml();
}

2. A function should do one thing. Do it well. Just do it

3. Use descriptive names

Don't be afraid of long names. Long and descriptive names are better than short and confusing ones.

4. Function parameters

The most ideal number of parameters is zero, followed by one, and then two. Three parameter functions should be avoided as much as possible. The more parameters, the more difficult it is to understand, the more error prone it is to make a call.

// Read message and think it's expected?
// Can't figure out the order of expected and actual
assertEquals(message, expected, actual) 

• identification parameters are not transmitted. Passing in a Boolean value to a function is appalling, declaring that the function does more than one thing.
• avoid using output parameters and use return results instead of modifying input parameters.
• if a function requires two, three, or more parameters, some of them should be encapsulated as classes.

5. Use exception instead of return error code

When an error code is returned, the caller is asked to handle the error immediately.

if(deletePage(page) == E_OK) {
    if(registry.deleteReference(page.name) == E_OK) {
        if(configKeys.deleteKey(pange.name.makeKey()) == E_OK) {
            logger.log("page deleted");
        } else {
            logger.log("configKey not deleted");
        }
    } else {
        logger.log("deleteReference from registry failed");
    }
} else {
    logger.log("delete failed");
    return E_ERROR;
}

On the other hand, if the return error code is replaced by an exception, the error handling code can be separated from the main path code and simplified:

try {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
} catch (Exception e) {
    logger.log(e.getMessage());
}

6. Don't repeat yourself

Duplication may be the root of all evils in software. Many principles and practical rules are created to control and eliminate duplication.

7. How to write such a function

• writing code is very similar to writing something else. When you write a paper or article, you write what you want first, and then polish it. The first draft may be rough and out of order, so you'll have to think about it until it's what you think it is.
• when writing functions, they are lengthy and complex at first. There are too many indents and nested loops. There is an overly long parameter list. The name is also optional, and there will be duplicate code. But with a set of unit tests, cover every ugly line of code.
• then polish the code, break down the function, change the name, and eliminate duplication. Shortening and relocation methods. Sometimes we have to break them up. At the same time, keep the test pass.
• finally, follow the rules listed in this chapter to assemble these functions.
Don't write functions according to the rules from the beginning; no one can.

Chapter 4 notes

1. Annotation is a double-edged sword

Nothing is more useful than putting good comments. Nothing can mess up a module more than a mess of annotations. Nothing can be more destructive than stale, misinformed comments. (note is a double-edged sword)

2. Comments are used to make up for the lack of code expression ability

Comments don't beautify bad code, they illustrate the work with code.

3. The author strongly belittles the notes

Because annotations lie, he thinks programmers can't insist on maintaining them. Although sometimes we need comments, we should also try our best to reduce the amount of comments.

4. Good notes

• legal information

// Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved

• notes to provide information

// Returns an instance of the Responder being tested.
protected abstract Responder responderInstance();

• interpretation of intent

// This is our best attempt to get a race condition
// by creating large number of threads
for(int i =0; i<25000; i++) {
    Thread thread = new Thread(widgetBuilderThread);
    thread.start();
}

• warning

public static SimpleDateFormat makeStandardHttpDateFormat() {
    // SimpleDateFormat is not thread safe
    // so we need to create each instance independently.
    SimpleDateFormat df = new SimpleDateFormat("EEE, dd MM yyy HH:mm:ss z");
    df.setTimeZone(TimeZone.getTimeZone("GTM"));
    return df;
}

• TODO notes
• Javadoc in public API

7. Bad notes

• redundant notes

// Utility method that returns when this.closed is true. Throws an exception if
// the timeout is reached
public synchronized void waitForClose(final long timeoutMillis) Throws Exception {
    if(!closed) {
        wait(timeoutMillis);
        if(!closed) {
            throw new Exception("MockResponseSender could not be closed");
        }
    }
}

This comment does not provide more information than the code itself, nor is it easier to read than the code itself.
• track notes
The rule that every function must have Javadoc or every variable must have comments is completely ridiculous.
· log notes

* Changes (from 11-oct-2011)
* --------------------------
* 11-Oct-2001: Re-organised the class and moved it to new package com.jrefiner.date
* 05-Nov-2001: Added a getDescription() method, and eliminated NotableDate class

• location marking
Sometimes, programmers like to mark a particular place in the source code.

// Actions ////////////////////////////////////////////////////////

• commented out code
It's annoying to comment out the code directly. Don't do that.

InputStreamResponse response = new InputStreamResponse();
response.setBody(formatter.getResultStream(), formatter.getByteCount());
// InputStream resultStream = formatter.getResultStream();
// StreamReader reader = new StreamReader(resultStream);
// response.setContent(reader.read(formatter.getByteCount());

Other people dare not delete the commented code. They will think that there must be a reason why the code is still there. Moreover, this code is very important and cannot be deleted.

Chapter 5 format

1. Team format specification

If you work in a team, the team should agree to a simple set of format rules that all members should follow.

2. Vertical format

The called function should be placed under the function executing the call. This creates a good flow of information from the top down through the source code module.

3. Variable declaration should be as close to its use position as possible

4. Entity variables should be declared at the top of the class

Chapter 6 object and data structure

1. Object and data structure

Data structure: exposes its data without providing meaningful functions

public class Point {
    public double x;
    public double y;
}

Object: the function that exposes the operation data after hiding the data in the abstract

public class Point {
    private double x;
    private double y;
    double getX();
    double getY();
    void setCartesian(double x, double y);
}

2. Procedural code and object-oriented code

Process code:

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.14159265358;
    
    public double area(Object shape) throws NoSuchShapeException {
        if(shape instanceof Square) {
            Square s = (Square)shape;
            return s.side * s;
        } else if(shape instanceof Rectangle) {
            Rectanle r = (Rectanle)shape;
            return r.height * r.width;
        } else if(shape instanceof Circle) {
            Circle c = (Circle)shape;
            return PI * c.radius * c.radisu;
        }
        throw new NoSuchShapeException();
    }
}

Object oriented code:

public class Square implements Shape {
    private Point topLeft;
    private double side;
    
    public double area() {
        return side * side;
    }
}
public class Rectangle implements Shape {
    private Point topLeft;
    private double height;
    private double width;
    
    public double area() {
        return height * width;
    }
}
public class Circle implements Shape {
    private Point center;
    private double radius;
    public final double PI = 3.14159265358;
    
    public double area() {
        return PI * radius * radius;
    }
}

3. Procedural code vs. object-oriented code

Process code:
• add new functions: shape classes are not affected at all
• add a new class: you have to modify all the functions in Geometry to handle it
Object oriented code:
Add new function: all shape classes need to be modified
• add new classes: none of the existing functions will be affected
The essence of these two definitions is diametrically opposed. Procedural code (code using data structure) is convenient for adding new functions without changing the existing data structure; object-oriented code is convenient for adding new classes without changing the existing functions.
All objects are just a legend. Sometimes you really want to do some procedural operations on simple data structures.

4. The law of Demeter

Method f of class C should only call methods of the following objects:
• C
• objects created by f;
• the object passed to f as a parameter;
• objects held by entity variables of C.
Method should not call the method of an object returned by any function.

final String outputDir = ctx.getOptions().getScrachDir().getAbsolutePath();

Chapter 7 error handling

1. Use exception instead of return code

Long ago, many languages did not support exceptions (C language). These languages have limited means of handling and reporting errors. You can either set an error ID or return the code to the caller to check for errors.
The problem with these methods is that they mess up the caller code, and the caller has to check for errors immediately after the call. Unfortunately, this step is easy to forget.

2. Use unchecked exception

The price of controllable exception is to violate the open / closed principle. If you throw a controllable exception in a method, and the catch statement is above three levels, you have to declare the exception in each method signature between the catch statement and the place where the exception was thrown. This means that the modification of the lower level in the software will affect the signature of the higher level.

3. Do not return null value

Returning a null value is basically adding work to yourself, and also adding confusion to the caller. As long as there is one place where null values are not checked, the application can get out of control.

public void registerItem(Item item) {
    if (item != null) {
        ItemRegistry registry = peristentStore.getItemRegistry();
        if (registry != null) {
            Item existing = registry.getItem(item.getID());
            // Without checking null, what happens?
            if(existing.getBillingPeriod().hasRetailOwner()) {
                existing.register(item);
            }
        }
    }
}    

It's better to throw an exception or return a special case object instead of returning a null value.

Chapter 8 boundary

Learning tests: write tests to review and understand third-party code.

@Test
public void testLogCreate() {
    Logger logger = Logger.getLogger("MyLogger");
    logger.info("hello");
}

When an error occurs, it tells us that we need to use appender - > to read the document, find a ConsoleAppender, and add ConsoleAppender

@Test
public void testLogCreate() {
    Logger logger = Logger.getLogger("MyLogger");
    ConsoleAppender appender = new ConsoleAppender();
    logger.addAppender(appender);
    logger.info("hello");
}

No output stream found for Appender. When Google gets help, it adds patternLayout:

@Test
public void testLogCreate() {
    Logger logger = Logger.getLogger("MyLogger");
    logger.removeAllAppenders();
    ConsoleAppender appender = new ConsoleAppender(
        new PatternLayout("%p %t %m%n"), ConsoleAppender.SYSTEM_OUT
    );
    logger.addAppender(appender);
    logger.info("hello");
}

Correct, output hello to the console!

Chapter 9 unit test

1. Keep the test clean

Dirty testing is the same as - if not worse than - not testing. Tests must be modified as production code evolves, and the dirtier the test, the harder it is to modify. Test code is as important as production code.
If tests don't stay clean, you lose them. Without testing, you'll lose everything you need to make your production code scalable. It's unit testing that makes your code scalable, maintainable, and reusable!

2. Three elements of neatness test: readability, readability and readability

public void testGetPageHieratchyAsXml() throws Exception {
  crawler.addPage(root, PathParser.parse("PageOne"));
  crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
  crawler.addPage(root, PathParser.parse("PageTwo"));
  request.setResource("root");
  request.addInput("type", "pages");
  Responder responder = new SerializedPageResponder();
  SimpleResponse response =
    (SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
  String xml = response.getContent();
  assertEquals("text/xml", response.getContentType());
  assertSubString("<name>PageOne</name>", xml);
  assertSubString("<name>PageTwo</name>", xml);
  assertSubString("<name>ChildOne</name>", xml);
}

public void testGetPageHieratchyAsXmlDoesntContainSymbolicLinks() throws Exception {
  WikiPage pageOne = crawler.addPage(root, PathParser.parse("PageOne"));
  crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
  crawler.addPage(root, PathParser.parse("PageTwo"));
  PageData data = pageOne.getData();
  WikiPageProperties properties = data.getProperties();
  WikiPageProperty symLinks = properties.set(SymbolicPage.PROPERTY_NAME);
  symLinks.set("SymPage", "PageTwo");
  pageOne.commit(data);
  request.setResource("root");
  request.addInput("type", "pages");
  Responder responder = new SerializedPageResponder();
  SimpleResponse response =
    (SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
  String xml = response.getContent();
  assertEquals("text/xml", response.getContentType());
  assertSubString("<name>PageOne</name>", xml);
  assertSubString("<name>PageTwo</name>", xml);
  assertSubString("<name>ChildOne</name>", xml);
  assertNotSubString("SymPage", xml);
}

Tests are hard to read:
• there are a number of horrible duplicate code calls addPage and addSubString.
• the code is full of details that interfere with test expressiveness.
restructure:

public void testGetPageHierarchyAsXml() throws Exception {
  makePages("PageOne", "PageOne.ChildOne", "PageTwo");
  submitRequest("root", "type:pages");
  assertResponseIsXML();
  assertResponseContains(
    "<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>");
}

public void testSymbolicLinksAreNotInXmlPageHierarchy() throws Exception {
  WikiPage page = makePage("PageOne");
  makePages("PageOne.ChildOne", "PageTwo");
  addLinkTo(page, "PageTwo", "SymPage");
  submitRequest("root", "type:pages");
  assertResponseIsXML();
  assertResponseContains(
    "<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>");
  assertResponseDoesNotContain("SymPage");
}

Chapter 10

1. Class should be short

The first rule of a class is that it should be short. The second rule is to be shorter.

2. Single responsibility principle (SRP)

Class or module should have and only have one reason for modification.

Chapter 11 system

1. Separate the construction and use of the system

The software system shall separate the start-up process from the run-time logic after the start-up process.

2. Dependency injection

There is a powerful mechanism to separate construction and use, that is, Dependency Injection

Chapter 12 iteration

Four rules for simple design are as follows:

  1. run all tests
  2. Non repeatable
  3. Express programmer's intention
    • express by choosing a good name
    • expressed by keeping function and class sizes short
    • express by using standard nomenclature
    • well written test units are also expressive
  4. Minimize the number of classes and methods

Posted by falcons on Tue, 23 Jun 2020 00:13:29 -0700