Skip to content

Universal & language-specific refactoring techniques in programming.

Notifications You must be signed in to change notification settings

enginooby-academics/code-refactoring

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Alternative ways of writing code to improve code readability, clarity, brevity & performance

Table of Contents

Language-Specific

C# C# in Unity C++ C++ in Unreal Engine Java Dart Python R JS/TS Go

Declaration, Initialization & Assignment

  • Type annotation (explicit type) => type inference (implicit type) for initializer expression and local variable [C++11 auto, C# var, TS let, Dart var]

Pros: shorthand especially for long type init, focus more attention on local variable name and value

  • Remove redundant member initializer (with default value)
// πŸ‘Ž non-compliant
private int _level = 0;
private bool _isGameOver = false;

// πŸ‘ preference
private int _level;
private bool _isGameOver;

Initializer expression:

// πŸ‘Ž longhand
AReallyReallyLooooongClass instance = new AReallyReallyLooooongClass();

// πŸ‘ shorthand
var instance = new AReallyReallyLooooongClass();

For local variable:

// πŸ‘‰ given
List<User> users = {User1, User2...};

// πŸ‘Ž non-compliant
foreach(User user in users)

// πŸ‘ preference
foreach(var user in users)
  • Assign w/ nullable variable
// πŸ‘Ž longhand
const result = (nullableVar !== null && nullableVar !== undefined) ? nullableVar : fallbackVar

Using nullish coalescing operator ?? [C#, PHP, ES11]

// πŸ‘ shorthand
const result = nullableVar ?? fallbackVar

Using short circuit evaluation &&, ||

// πŸ‘ shorthand
const result = nullableVar || fallbackVar
  • Assign default value for nullable variable using logical nullish assigment operator ??= [TS/JS, C#8]
// πŸ‘Ž longhand
nullableVar = (nullableVar !== null && nullableVar !== undefined) ? nullableVar : defaultVal

// πŸ‘Ž longhand
nullableVar ?? (nullableVar = defaultVal)

// πŸ‘ shorthand
nullableVar ??= defaultVal
  • Multiple variable declaration for related variables (and usually same type) [JS/TS, C#, Java, Go]
// πŸ‘Ž longhand
private string _firstName;
private string _lastName;
private float _height;
private float _weight;

// πŸ‘ shorthand
private string _firstName, _lastName;
private float _height, _weight;
  • Assign multiple variables using object destructuring/tuple [ES6, C#, Python]
// πŸ‘Ž longhand
let a = 1;
let b = 2;
let c = 3;

// πŸ‘ shorthand
[a, b, c] = [1, 2, 3]
  • Swap two variables using XOR ^

βœ”οΈ Pros: avoid using third temporary variable
❌ Cons: purpose may not seem straightforward

// πŸ‘Ž longhand
temp = a;
a = b;
b = temp;

// πŸ‘ shorthand
a ^= b ^= a ^= b;

Control Flow

  • Guard clause/assert/precondition: early return for special case; multiple return statements

βœ”οΈ Pros: avoid nested statements, improve readability
⚠️ Cautions: too many return statements (esp. void) will make code unobvious and harder to debug

// πŸ‘Ž non-compliant
function getInsuranceDeductible(insurance){
  if (insurance.covered) {
    if (insurance.majorRepair) {
      return 500
    } else if (insurance.mediumRepair){
      return 300
    } else {
      return 100
    }
  } else {
    return 0
  }
}

// πŸ‘ preference (use daisy chaining ternary operator to shorten furthermore)
function getInsuranceDeductible(insurance){
  if (!insurance.covered) return 0
  if (insurance.majorRepair) return 500
  if (insurance.mediumRepair) return 300

  return 100
}
  • Ternary operator ? :

To assign value:

// πŸ‘Ž longhand
if (a > b) {
    result = x;
}
else {
    result = y;
}

// πŸ‘ shorthand
result = a > b ? x : y;

To return:

// πŸ‘Ž longhand
if (a > b) {
    return x;
}
else {
    return y;
}

// πŸ‘ shorthand
return a > b ? x : y;

To invoke function:

// πŸ‘Ž longhand
if (target is not null) {
    print("Target found.");
}
else {
    print("Target not found.");
}

// πŸ‘ shorthand
print(target is not null ? "Target found." : "Target not found.");

To shorten switch w/ multiple return statements using daisy chaining:

// πŸ‘Ž longhand
function getInsuranceDeductible(insurance) {
  if (!insurance.covered) return 0
  if (insurance.majorRepair) return 500
  if (insurance.mediumRepair) return 300

  return 100
}

// πŸ‘ shorthand
function getInsuranceDeductible(insurance) {
  return insurance.covered ? 0
         : insurance.majorRepair ? 500
         : insurance.mediumRepair ? 300 
         : 100
}
  • Conditional w/ falsy/truthy values [TS/JS, Groovy, Perl, PHP, Python, Ruby, C++ 0, false]
// πŸ‘Ž longhand
if(typeof a !== "undefined" && typeof b !== "null" && typeof c !== "NaN" && d !== "" && array.length !== 0)

// πŸ‘ shorthand
if(a && b && c && d && array.length)
  • One-statement in conditional w/ short circuit evaluation &&, ||
// πŸ‘Ž longhand
if(isHungry && isSleepy) {
  code()
}

// πŸ‘ shorthand
isHungry && isSleepy && code()

Expressions

  • Spaceship/three-way comparison operator <=> [C++, Groovy, Kotlin, Perl, PHP, Ruby]
// πŸ‘‰ given
$users = ['branko', 'ivana', 'luka', 'ivano'];

// πŸ‘Ž longhand
usort($users, function ($a, $b) {
  return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);
});

// πŸ‘ shorthand
usort($users, function ($a, $b) {
  return $a <=> $b;
});

OOP

  • Constructor shorthand [TS, PHP8 (property promotion), Dart]
// πŸ‘Ž longhand
class User {
  private name: string;
  private age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

// πŸ‘ shorthand
class User {
  constructor(
    private name: string,
    private age: number
  ) {}
}
  • Explicitly declare access modifiers for class members

Reason: clarify purpose of the member

class User {
  // πŸ‘Ž non-compliant
  name: string;
  
  // πŸ‘ preference
  public name: string;
}

Function

  • Use lambda expression/arrow function => to declare single statement functions [Java8, JS/TS (fat arrow), Dart, C# (expression-bodied members), Python lambda]

Pros: bind this to the invoker

// πŸ‘Ž longhand
function getSum(a: number, b: number) {
  return a + b
}

// πŸ‘ shorthand
const getSum = (a: number, b: number) => (a + b)
  • Pass a single-use callback function as an argument w/ anonymous function

Pros: no need to declare a separate disposable function -> reduce coding overheating, often used along w/ collection operation such as map(), where(), and reduce(), etc

  • Return null => empty collection

Reason: less error prone, reduce null checking on function usage

  • Named argument [PHP8, Kotlin, C#, Python (keyword argument)]

Reason: for clarity, order of params could be changed, convenient to leave default arguments

// πŸ‘‰ given
public void doSomething(string foo, int bar) {...}

// πŸ‘Ž non-compliant
doSomething("someString", 1);

// πŸ‘ preference
doSomething(foo: "someString", bar: 1);
  • Overloading functions that differ only in trailing parameters => optional parameters
// πŸ‘Ž non-compliant
interface Example {
  diff(one: string): number;
  diff(one: string, two: string): number;
  diff(one: string, two: string, three: boolean): number;
}

// πŸ‘ preference
interface Example {
  diff(one: string, two?: string, three?: boolean): number;
}
  • Promise/callback chaining => async-await [ES6, C#, Dart]

String

  • String => StringBuffer for string manipulations [Java, C#]

Reason: do not create new String instances on manipulations -> save memory

  • String concatenation operator + => String interpolation/template literals `${}` [ES, C#, Kotlin, PHP]
// πŸ‘Ž non-compliant
console.log("Sum of " + a + " and " + b + " is " + (a + b))

// πŸ‘ preference
console.log(`Sum of ${a} and ${b} is ${a + b}`)

Releases

No releases published

Packages

No packages published