Skip to content

longporo/cleancode

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 

Repository files navigation

Clean Code

Clean = Readable + Understanding

Naming

Common prefix

  • is

  • can

  • has/icludes/contains

  • should/needs

Create/Destroy

Create Create an instance, used in instantiated and factory methods
Initialize Initializes the properties and settings for the instance
Uninitialize Clean up the properties and Settings of the instance, as opposed to Initialize
Load Load content from the configuration
Destroy Destroy instance

Get/Set

Get Get properties, things you can get directly
Fetch Data from network
Calculate Data need to be calculated
Read Data from files or configurations
Query Data from DB
Find Data from DB or list container
Receive Receive files or messages
Pull Pull
Set Set properties
Write Write into files or configuration
Put Put in
Push Save in, push messages

Update

Reset Reset sign or status
Refresh Refresh page or cache
Update Update configuration or status

Add/Remove

Add Add things
Append Add to the tail
Insert Insert someplace
Delete Delete ≈ Remove
Remove Remove ≈ Delete

Start/Stop

Open Open file
Start Start workflow
Launch Launch program or server
Close Close file
Stop Stop workflow
Pause Pause workflow which would start again
Finish Workflow done

Handle data from list

Filter Filter data with conditions
Merge Merge data with conditions
Concat Add to the tail
Split Split
Deduplicate Remove duplicate
Reverse Reverse
Sort Sort
Fill Overwrite data

Common verbs

Parse Parse to a specific format, parse and extract content
Analyse Data that can  not easily get
Convert Convert to another type
Format Format data
Validate Data Verification
Ensure Validate the expected value
Compose Compose multiple parts to a result
Encode Encode
Decode Decode
Encrypt Encrypt
Decrypt Decrypt
Backup Backup
Restore Restore
Import Import from a special format file
Export Export to a special format file
Compress Compress
Decompress Decompress

Functions

Clean functions should be small and do only one thing

Parameters

Keep the number of parameters small. Use a dictionary or Object to group multiple parameters

Level of abstraction

Do not mix levels of abstraction in a function

// Need to understand and interpret the different steps
if(!email.includes('@')) {
  console.log('Invalid email');
} else {
  const user = new User(email);
  user.save();
}
// Just need to read the different steps
if(!isEmail(email)) {
  showError('Invalid email');
} else {
  saveNewUser(email);
}

Pure functions

  • Same input always returns same result

  • No side effects during execution(console, change the external data, API calling)

// Not a pure function
function connectDB() {
  const didConnect = database.connnect();
  if(didConnect) {
    return true;
  } else {
    console.log('Could not connect to database!');
    return false;
  }
}
// Optimize to a pure function
function initApp() {
  const success = connectDB();
  if(!success) {
    console.log('Could not connect to database!');
  }
  // ...
}

function connectDB() {
  const didConnect = database.connnect();
  return didConnect;
}

Test driven functions

// Hard to test the method partially
function addProduct(name, price) {
    if(!name || name.trim() === '' || !price || price < 0 ) {
        console.log('Invalid input - product was not created.');
        return;
    }
    
    const product = {
        id: name + '-' + Math.random().toString(),
        name: name,
        price: price
    };
    database.insert('products', product);
    return product;
}
// Split each process
function addProduct(name, price) {
    validateProductData(name, price);

    const savedProduct = saveProduct(name, price);
    return savedProduct;
}

function validateProductData(name, price) {
    if(!inputIsValid(name, price)) {
        throw new Error('Invalid input - product was not created.');
    }
}

function inputIsValid(name, price) {
    return !isEmpty(name) && hasMinValue(price, 0);
}

Do not use boolean parameters

// Call methods, hard to understand
printWelcomeWords(true);
calFinalAmount(200, true);

function printWelcomeWords(isActivated) {
    if (isActivated) {
        console.log('Welcome! Please select your product.');
    } else {
        console.log('Welcome! Please activate your account.');
    }
}

function calFinalAmount(originalAmount, isChild) {
    if (isChild) {
        return originalAmount * 0.5;
    } else {
        return originalAmount;
    }
}
// Call methods, easy to understand
printWelcomeWordsOnActivated();
calFinalAmountForChild(200);
calFinalAmountForAdult(200);

function printWelcomeWordsOnActivated() {
    console.log('Welcome! Please select your product.');
}

function printWelcomeWordsOnUnactivated() {
    console.log('Welcome! Please activate your account.');
}

function calFinalAmountForChild(originalAmount) {
    return originalAmount * 0.5;
}

function calFinalAmountForAdult(originalAmount) {
    return originalAmount;
}

Objects

Law of Demeter

  • Method within a class only interact with its direct friends
    • The variables, method parameters and method return objects within the class
  • The local variables are not direct friends.
// Violates the Law of Demeter, interact with local variable: Employee
public class Company {
    private Manager manager = new Manager();
    
    public void printEmployees(String name) {
        List<Employee> employeeList = manager.getEmployees(name);
        for(Employee employee : employeeList) {
            System.out.println(employee.getName());
        }
    }
}
// Follows the Law of Demeter.
public class Company {
    private Manager manager = new Manager();
    
    public void printEmployees(String name) {
        manager.printEmployee(name);
    }
}

SOLID principle

  • 'S' and 'O' make code clean
    • Single responsibility Principle
      • each class does one thing, as small as possible
    • Open-closed Principle
      • a class should be open for extension but closed for modification
      • Reduce redundant code
  • High cohesion
    • how well the methods of a class are using the class properties

Strategy design pattern

let mapCalculate = {
    'add': function (num1, num2) {
        return num1 + num2;
    },
    'sub': function (num1, num2) {
        return num1 - num2;
    },
    'mul': function (num1, num2) {
        return num1 * num2;
    },
    'div': function (num1, num2) {
        return num1 / num2;
    },
}

function calculate(command, num1, num2) {
    return mapCalculate[command](num1, num2);
}
class Calculator {
    constructor() {
        this.strategy = null;
    }
    
    // Strategy can be changed dynamically
    setStrategy(strategy) {
        this.strategy = strategy;
    }
    
    calculateResult(num1, num2) {
        return this.strategy.execute(num1, num2);
    }
}

class Add {
    execute(num1, num2) {
        return num1 + num2;
    }
}

class Sub {
    execute(num1, num2) {
        return num1 - num2;
    }
}

//...

Tips

Lines

Keep lines short

Comments

  • Good code does not need too many comments
  • Only use when:
    • Explanations that can’t be replaced by good naming
    • Warnings
    • Todo notes

Elegant string concatenation

// C++: snprintf, stringstream
// C#: string.Format
// Java: String.format

let userId = 'foo';
let userName = 'bar';
let userInfo = 'User Info: Id: ${userId} Name: ${userName}.';
let headerContent = 'start | ${userId} | ${userName} | end';

About

A summary of writing clean code

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published