Skip to content

Instantly share code, notes, and snippets.

@revskill10
Created March 8, 2023 04:23
Show Gist options
  • Save revskill10/01a30c460abbb2b6d4fbb5737cd3ba97 to your computer and use it in GitHub Desktop.
Save revskill10/01a30c460abbb2b6d4fbb5737cd3ba97 to your computer and use it in GitHub Desktop.
//Extract fragment of code into its own function named after its purpose.
function printOwing(invoice) {
printBanner()
let outstanding = calculateOutstanding()
// print details
console.log(`name: ${invoice.customer}`)
console.log(`amount: ${outstanding}`)
}
//to
function printOwing(invoice) {
printBanner()
let outstanding = calculateOutstanding()
printDetails(outstanding)
function printDetails(outstanding) {
console.log(`name: ${invoice.customer}`)
console.log(`amount: ${outstanding}`)
}
}
//Get rid of the function when the body of the code is just as clear as the name
function rating(aDriver) {
return moreThanFiveLateDeliveries(aDriver) ? 2 : 1
}
function moreThanFiveLateDeliveries(aDriver) {
return aDriver.numberOfLateDeliveries > 5
}
//to
function rating(aDriver) {
return aDriver.numberOfLateDeliveries > 5 ? 2 : 1
}
//Add a name to an expression
//price is base price ­ quantity discount + shipping
return order.quantity * order.itemPrice -
Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +
Math.min(order.quantity * order.itemPrice * 0.1, 100)
//to
const basePrice = order.quantity * order.itemPrice
const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemP
const shipping = Math.min(basePrice * 0.1, 100)
return basePrice - quantityDiscount + shipping;
//Remove variable which doesn't really communicate more than the expression itself.
let basePrice = anOrder.basePrice
return (basePrice > 1000)
//to
return anOrder.basePrice > 1000
//Rename a function, change list of parameters
function circum(radius) {...}
//to
function circumference(radius) {...}
//Encapsulate a reference to some data structure
let defaultOwner = {firstName: "Martin", lastName: "Fowler"}
//to
// defaultOwner.js
let defaultOwnerData = {firstName: "Martin", lastName: "Fowler"}
export function defaultOwner() { return defaultOwnerData }
export function setDefaultOwner(arg) { defaultOwnerData = arg }
//Make shared variable's name can self-explain
let a = height * width
//to
let area = height * width
//Replace groups of data items that regularly travel together with a single data structure
function amountInvoiced(startDate, endDate) {}
function amountReceived(startDate, endDate) {}
function amountOverdue(startDate, endDate) {}
//to
function amountInvoiced(aDateRange) {}
function amountReceived(aDateRange) {}
function amountOverdue(aDateRange) {}
//Form a class base on group of functions that operate closely on a common data
function base(aReading) {}
function taxableCharge(aReading) {}
function calculateBaseCharge(aReading) {}
//to
class Reading() {
base() {}
taxableCharge() {}
calculateBaseCharge() {}
}
//Takes the source data as input and calculates all the derivations, putting each derived value as a field in the output data
function base(aReading) {}
function taxableCharge(aReading) {}
//to
function enrichReading(argReading) {
const aReading = _.cloneDeep(argReading)
aReading.baseCharge = base(aReading)
aReading.taxableCharge = taxableCharge(aReading)
return aReading
}
//Split code which do different things into separate modules
const orderData = orderString.split(/\s+/)
const productPrice = priceList[orderData[0].split("-")[1]]
const orderPrice = parseInt(orderData[1]) * productPrice
//to
const orderRecord = parseOrder(orderString)
const orderPrice = price(orderRecord, priceList)
function parseOrder(aString) {
const values = aString.split(/\s+/)
return {
productID: values[0].split("-")[1],
quantity: parseInt(values[1])
}
}
function price(order, priceList) {
return order.quantity * priceList[order.productID]
}
//Create record (class) from object
organization = {name: "Acme gooseberries", country: "GB"}
//to
class Organization {
constructor(data) {
this._name = data.name
this._country = data.country
}
get name() { return this._name }
set name(arg) { this._name = arg }
get country() { retrun this._country }
set country(arg) { this._country = arg }
}
//A method returns a collection. Make it return a read-only view and provide add/remove methods
class Person {
get courses() { return this._courses }
set courses(aList) { this._courses = aList }
}
//to
class Person {
get courses() { return this._courses.slice() }
addCourse(aCourse) {}
removeCourse(aCourse)
}
//Create class for data
orders.filter(o => "high" === o.priority || "rush" === o.priority)
//to
orders.filter(o => o.priority.higherThan(new Priority("normal")))
//Extract the assignment of the variable into a function
const basePrice = this._quantity * this._itemPrice
if (basePrice > 1000) {
return basePrice * 0.95
} else {
return basePrice * 0.98
}
//to
get basePrice() { return this._quantity * this._itemPrice }
//...
if (this.basePrice > 1000) {
return this.basePrice * 0.95
} else {
return this.basePrice * 0.98
}
//Extract class base on a subset of data and a subset of methods
class Person {
get officeAreaCode() { return this._officeAreaCode }
get officeNumber() { return this._officeNumber }
}
//to
class Person {
get officeAreaCode() { return this._telephoneNumber.areaCode }
get officeNumber() { return this._telephoneNumber.number }
}
class TelephoneNumber {
get areaCode() { return this._areaCode }
get number() { return this._number }
}
//Merge class if class isn't doing very much. Move its feature to another class then delete it.
class Person {
get officeAreaCode() { return this._telephoneNumber.areaCode }
get officeNumber() { return this._telephoneNumber.number }
}
class TelephoneNumber {
get areaCode() { return this._areaCode }
get number() { return this._number }
}
//to
class Person {
get officeAreaCode() { return this._officeAreaCode }
get officeNumber() { return this._officeNumber }
}
//A client is calling a delegate class of an object, create methods on the server to hide the delegate.
manager = aPerson.department.manager
//to
manager = aPerson.manager
class Person {
get manager() {
return this.department.manager
}
}
//Client call the delegate directly
manager = aPerson.manager
class Person {
get manager() {
return this.department.manager
}
}
//to
manager = aPerson.department.manager
//Replace complicated algorithm with simpler algorithm
function foundPerson(people) {
for (let i = 0; i < people.lenth; i++) {
if (people[i] === "Don") {
return "Don"
}
if (people[i] === "John") {
return "John"
}
if (people[i] === "Kent") {
return "Kent"
}
}
return ""
}
//to
function foundPerson(people) {
const candidates = ["Don", "John", "Kent"]
return people.find(p => candidates.includes(p)) || ""
}
//Move a function when it references elements in other contexts more than the one it currently resides in
class Account {
get overdraftCharge() {}
}
class AccountType {}
//to
class Account {}
class AccountType {
get overdraftCharge() {}
}
//Move field from once class to another
class Customer {
get plan() { return this._plan }
get discountRate() { return this._discountRate }
}
//to
class Customer {
get plan() { return this._plan }
get discountRate() { return this.plan.discountRate }
}
//When statement is a part of called functions (always go togeter), move it inside the function
result.push(`<p>title: ${person.photo.title}</p>`)
result.concat(photoData(person.photo))
function photoData(aPhoto) {
return [
`<p>location: ${aPhoto.location}</p>`,
`<p>date: ${aPhoto.data.toDateString()}</p>`
]
}
//to
result.concat(photoData(person.photo))
function photoData(aPhoto) {
return [
`<p>title: ${aPhoto.title}</p>`,
`<p>location: ${aPhoto.location}</p>`,
`<p>date: ${aPhoto.data.toDateString()}</p>`
]
}
//4. Move Statements To Callers
emitPhotoData(outStream, person.photo)
function emitPhotoData(outStream, photo) {
outStream.write(`<p>title: ${photo.title}</p>\n`)
outStream.write(`<p>location: ${photo.location}</p>\n`)
}
//to
emitPhotoData(outStream, person.photo)
outStream.write(`<p>location: ${photo.location}</p>\n`)
function emitPhotoData(outStream, photo) {
outStream.write(`<p>title: ${photo.title}</p>\n`)
}
//Replace the inline code with a call to the existing function
let appliesToMass = false
for (const s of states) {
if (s === "MA") appliesToMass = true
}
//let appliesToMass = false
//for (const s of states) {
// if (s === "MA") appliesToMass = true
//}
appliesToMass = states.includes("MA")
//Move related code to near each other
const pricingPlan = retrievePricingPlan()
const order = retreiveOrder()
let charge
const chargePerUnit = pricingPlan.unit
//to
const pricingPlan = retrievePricingPlan()
const chargePerUnit = pricingPlan.unit
const order = retreiveOrder()
let charge
//Split the loop which does two different things
let averageAge = 0
let totalSalary = 0
for (const p of people) {
averageAge += p.age
totalSalary += p.salary
}
averageAage = averageAge / people.length
//to
let totalSalary = 0
for (const p of people) {
totalSalary += p.salary
}
let averageAge = 0
for (const p of people) {
averageAge += p.age
}
averageAge = averageAge / people.length
//Replace loop with collection pipeline, like map or filter
const names = []
for (const i of input) {
if (i.job === "programmer") {
names.push(i.name)
}
}
//to
const names = input.filter(i => i.job === "programmer").
map(i => i.name)
//9. Remove Dead Code
if (false) {
doSomethingThatUsedToMatter()
}
//to
//Any variable with more than one responsibility should be replaced with multiple variables, one for each responsibility
let temp = 2 * (height + width)
console.log(temp)
temp = height * width
console.log(temp)
//to
const perimeter = 2 * (height + width)
console.log(perimeter)
const area = height * width
console.log(area)
//2. Rename Field
class Organization {
get name() {}
}
//to
class Organization {
get title() {}
}
//Remove anny variables which cloud be easily calculate
get discountedTotal() { return this._discountedTotal }
set discount(aNumber) {
const old = this._discount
this._discount = aNumber
this._discountedTotal += old - aNumber
}
//to
get discountedTotal() { return this._baseTotal - this._discount }
set discount() { this._discount = aNumber }
//Treat data as value. When update, replace entire inner object with a new one
class Product {
applyDiscount(arg) {
this._price.amount -= arg
}
}
//to
class Product {
applyDiscount(arg) {
this._price = new Money(this._price.amount - arg, this._price.currency)
}
}
//When need to share an object in different place, or have duplicated objects
let customer = new Customer(customerData)
//to
let customer = customerRepository.get(customerData.id)
//Decomposing condition and replacing each chunk of code with a function call
if (!aDate.isBefore(plan.summerStart) && !aData.isAfter(plan.summerEnd)) {
charge = quantity * plan.summerRate
} else {
charge = quantity * plan.regularRate + plan.regularServiceCharge
}
//to
if (summer()) {
charge = summerCharge()
} else {
charge = regularCharge()
}
//Consolidate different condition check which the result action is same to a single condition check with single result
if (anEmployee.seniority < 2) return 0
if (anEmployee.monthsDisabled > 12) return 0;
if (anEmployee.isPartTime) return 0
//to
if (isNotEligableForDisability()) return 0
function isNotEligableForDisability() {
return ((anEmployee.seniority < 2)
|| (anEmployee.monthsDisabled > 12)
|| (anEmployee.isPartTime))
}
//If condition is unusual condition, early return (Guard Clauses) and exist the function
function getPayAmount() {
let result
if (isDead) {
result = deadAmount()
} else {
if (isSeparated) {
result = separatedAmount()
} else {
if (isRetired) {
result = retiredAmount()
} else {
result = normalPayAmount()
}
}
}
return result
}
//to
function getPayAmount() {
if (isDead) return deadAmount()
if (isSeparated) return separatedAmount()
if (isRetired) return retiredAmount()
return normalPayAmount()
}
//Using object oriented class instead of complex condition
switch (bird.type) {
case 'EuropeanSwallow':
return 'average'
case 'AfricanSwallow':
return bird.numberOfCoconuts > 2 ? 'tired' : 'average'
case 'NorwegianBlueParrot':
return bird.voltage > 100 ? 'scorched' : 'beautiful'
default:
return 'unknow'
}
//to
class EuropeanSwallow {
get plumage() {
return 'average'
}
}
class AfricanSwallow {
get plumage() {
return this.numberOfCoconuts > 2 ? 'tired' : 'average'
}
}
class NorwegianBlueParrot {
get plumage() {
return this.voltage > 100 ? 'scorched' : 'beautiful'
}
}
//Bring special check case to a single place
if (aCustomer === "unknown") {
customerName = "occupant"
}
//to
class UnknownCustomer {
get name() {
return "occupant"
}
}
//Make the assumption explicit by writing an assertion
if (this.discountRate) {
base = base - (this.discountRate * base)
}
//to
assert(this.discountRate >= 0)
if (this.discountRate) {
base = base - (this.discountRate * base)
}
//Separate function that returns a value (query only) and function with side effects (example: modify data)
function alertForMiscreant (people) {
for (const p of people) {
if (p === "Don") {
setOffAlarms()
return "Don"
}
if (p === "John") {
setOffAlarms()
return "John"
}
}
return ""
}
//to
function findMiscreant (people) {
for (const p of people) {
if (p === "Don") {
return "Don"
}
if (p === "John") {
return "John"
}
}
return ""
}
function alertForMiscreant (people) {
if (findMiscreant(people) !== "") setOffAlarms();
}
//Combine function with similar logic and different literal value
function tenPercentRaise(aPerson) {
aPerson.salary = aPerson.salary.multiply(1.1)
}
function fivePercentRaise(aPerson) {
aPerson.salary = aPerson.salary.multiply(1.05)
}
//to
function raise(aPerson, factor) {
aPerson.salary = aPerson.salary.multiply(1 + factor)
}
//Remove literal flag argument by clear name functions
function setDimension(name, value) {
if (name === 'height') {
this._height = value
return
}
if (name === 'width') {
this._width = value
return
}
}
//to
function setHeight(value) { this._height = value }
function setWidth(value) { this._width = value }
//Passing whole object instead of multiple parameters
const low = aRoom.daysTempRange.low
const high = aRoom.daysTempRange.high
if (aPlan.withinRange(low, high)) {}
//to
if (aPlan.withInRange(aRoom.daysTempRange)) {}
//5. Replace Parameter with Query
availableVacation(anEmployee, anEmployee.grade)
function availableVacation(anEmployee, grade) {
// calculate vacation...
}
//to
availableVacation(anEmployee)
function availableVacation(anEmployee) {
const grade = anEmployee.grade
// calculate vacation...
}
//Replace internal reference with a parameter
targetTemperature(aPlan)
function targetTemperature(aPlan) {
currentTemperature = thermostat.currentTemperature
// rest of function...
}
//to
targetTemperature(aPlan, thermostat.currentTemperature)
function targetTemperature(aPlan, currentTemperature) {
// rest of function...
}
//Make a field immutable by removing setting method
class Person {
get name() {...}
set name(aString) {...}
}
//to
class Person {
get name() {...}
}
//8. Replace Constructor with Factory Function
leadEngineer = new Employee(document.leadEngineer, 'E')
//to
leadEngineer = createEngineer(document.leadEngineer)
//Encapsulate function into its own object
function score(candidate, medicalExam, scoringGuide) {
let result = 0
let healthLevel = 0
// long body code
}
//to
class Scorer {
constructor(candidate, medicalExam, scoringGuide) {
this._candidate = candidate
this._medicalExam = medicalExam
this._scoringGuide = scoringGuide
}
execute() {
this._result = 0
this._healthLevel = 0
// long body code
}
}
//10. Replace Command with Function
class ChargeCalculator {
constructor (customer, usage){
this._customer = customer
this._usage = usage
}
execute() {
return this._customer.rate * this._usage
}
}
//to
function charge(customer, usage) {
return customer.rate * usage
}
//Move similar methods in subclass to superclass
class Employee {...}
class Salesman extends Employee {
get name() {...}
}
class Engineer extends Employee {
get name() {...}
}
//to
class Employee {
get name() {...}
}
class Salesman extends Employee {...}
class Engineer extends Employee {...}
//Pull up similar field to superclass to remove duplication
class Employee {...}
class Salesman extends Employee {
private String name;
}
class Engineer extends Employee {
private String name;
}
//to
class Employee {
protected String name;
}
class Salesman extends Employee {...}
class Engineer extends Employee {...}
//You have constructors on subclasses with mostly identical bodies.
//Create a superclass constructor; call this from the subclass methods
class Party {...}
class Employee extends Party {
constructor(name, id, monthlyCost) {
super()
this._id = id
this._name = name
this._monthlyCost = monthlyCost
}
}
//to
class Party {
constructor(name){
this._name = name
}
}
class Employee extends Party {
constructor(name, id, monthlyCost) {
super(name)
this._id = id
this._monthlyCost = monthlyCost
}
}
//If method is only relevant to one subclass, moving it from superclass to subclass
class Employee {
get quota {...}
}
class Engineer extends Employee {...}
class Salesman extends Employee {...}
//to
class Employee {...}
class Engineer extends Employee {...}
class Salesman extends Employee {
get quota {...}
}
//If field is only used in one subclass, move it to those subclasses
class Employee {
private String quota;
}
class Engineer extends Employee {...}
class Salesman extends Employee {...}
//to
class Employee {...}
class Engineer extends Employee {...}
class Salesman extends Employee {
protected String quota;
}
//6. Replace Type Code with Subclasses
function createEmployee(name, type) {
return new Employee(name, type)
}
//to
function createEmployee(name, type) {
switch (type) {
case "engineer": return new Engineer(name)
case "salesman": return new Salesman(name)
case "manager": return new Manager (name)
}
}
//You have subclasses do to little. Replace the subclass with a field in superclass.
class Person {
get genderCode() {return "X"}
}
class Male extends Person {
get genderCode() {return "M"}
}
class Female extends Person {
get genderCode() {return "F"}
}
//to
class Person {
get genderCode() {return this._genderCode}
}
//If 2 classes have similar behaviors, create superclass and move these behaviors to superclass
class Department {
get totalAnnualCost() {...}
get name() {...}
get headCount() {...}
}
class Employee {
get annualCost() {...}
get name() {...}
get id() {...}
}
//to
class Party {
get name() {...}
get annualCost() {...}
}
class Department extends Party {
get annualCost() {...}
get headCount() {...}
}
class Employee extends Party {
get annualCost() {...}
get id() {...}
}
//Merge superclass and subclass when there are no longer different enough to keep them separate
class Employee {...}
class Salesman extends Employee {...}
//to
class Employee {...}
//"Favor object composition over class inheritance" (where composition is effectively the same as delegation
class Order {
get daysToShip() {
return this._warehouse.daysToShip
}
}
class PriorityOrder extends Order {
get daysToShip() {
return this._priorityPlan.daysToShip
}
}
//to
class Order {
get daysToShip() {
return (this._priorityDelegate)
? this._priorityDelegate.daysToShip
: this._warehouse.daysToShip
}
}
class PriorityOrderDelegate {
get daysToShip() {
return this._priorityPlan.daysToShip
}
}
//If functions of the superclass don’t make sense on the subclass, replace with with delegate
class List {...}
class Stack extends List {...}
//to
class Stack {
constructor() {
this._storage = new List();
}
}
class List {...}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment