조건문 간결화 - ChoDragon9/posts GitHub Wiki
조건문 간결화
조건문 쪼개기(Decompose Conditional)
복잡한 조건문이 있을 때 각 부분을 메서드로 빼내자
// Before
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * _winterRate + _winterServiceCharge
} else {
charge = quantity * _summerRate
}
// After
const notSummer = (date) => date.before(SUMMER_START) || date.after(SUMMER_END)
const winterCharge = (quantity) => quantity * _winterRate + _winterServiceCharge
const summerCharge = (quantity) => quantity * _summerRate
if (notSummer(date)) {
charge = winterCharge(quantity)
} else {
charge = summerCharge(quantity)
}
중복 조건식 통합(Consolidate Conditional Experssion)
여러 조건 검사식의 결과가 같을 때 하나의 조건문으로 합친 후 메서드로 빼내자
// Before
const disabilityAmount = () => {
if (_seniority < 2) return 0
if (_monthDisabled > 12) return 0
if (_isPartTime < 2) return 0
}
// After
const isNotEligableForDisability = () => {
return _seniority < 2 || _monthDisabled > 12 || _isPartTime < 2
}
const disabilityAmount = () => {
if (isNotEligableForDisability()) return 0
}
조건문의 공통 실행 코드 빼내기(Consolidate Duplicate Conditional Fragments)
조건문의 모든절에 같은 실행 코드가 있을 때 같은 부분을 조건문 밖으로 빼자
// Before
if (isSpecialDeal()) {
total = price * 0.95
send()
} else {
total = price * 0.98
send()
}
// After
if (isSpecialDeal()) {
total = price * 0.95
} else {
total = price * 0.98
}
send()
제어 플래그 제거(Remove Control Frag)
논리 연산식의 제어 플래그 역할을 하는 변수가 있을 때 그 변수를 break문이나 return문으로 바꾸자
// Before
const checkSecurity = (people) => {
let found = ""
for (let i = 0; i < people.length; i++) {
if (!found) {
if(people[i] === "Don") {
sendAlert()
found = "Don"
}
if(people[i] === "John") {
sendAlert()
found = "John"
}
}
}
someAfterCode(found)
}
// After
const findMiscreant = (people) => {
for (let i = 0; i < people.length; i++) {
if (!found) {
if(people[i] === "Don") {
sendAlert()
return "Don"
}
if(people[i] === "John") {
sendAlert()
return "John"
}
}
}
return ""
}
const checkSecurity = (people) => {
let found = findMiscreant(people)
someAfterCode(found)
}
여러 겹의 조건문을 감시 절로 전환(Replace Nested Conditional with Guard Clauses)
메서드에 조건문이 있어서 정상적인 실행 결로를 파악하기 힘들 때 모든 특수한 경우에 감시 절을 사용하자
// Before
const getPayAmount = () => {
let result = null
if (_isDead) {
result = deadAmount()
} else {
if (_isSeparated) {
result = separatedAmount()
} else {
if (_isRetired) {
result = retiredAmount()
} else {
result = normalPayAmount()
}
}
}
return result
}
// After
const getPayAmount = () => {
if (_isDead) {
return deadAmount()
}
if (_isSeparated) {
return separatedAmount()
}
if (_isRetired) {
return retiredAmount()
}
return normalPayAmount()
}
조건문을 재정의로 전환(Replace Conditional with Polymorphism)
객체 타입에 따라 다른 기능을 실행하는 조건문이 있을 땐 조건문의 각 절을 하위 클래스의 재정의 메서드 안으로 옮기고, 원본 메서드는 abstract 타입으로 수정하자.
// Before
class Employee {
payAmount () {
switch (this.getType()) {
case EmployeeType.ENGINEER:
return this.getMonthlySalary()
case EmployeeType.SALESMAN:
return this.getMonthlySalary() + this.getCommission()
case EmployeeType.MANAGER:
return this.getMonthlySalary() + this.getBonus()
}
}
getType () {
return this.type
}
setType (type) {
this.type = type
}
}
// After
class Engineer {
payAmount (emp) {
return emp.getMonthlySalary()
}
}
class Salesman {
payAmount (emp) {
return emp.getMonthlySalary() + emp.getCommission()
}
}
class Manager {
payAmount (emp) {
return emp.getMonthlySalary() + emp.getBonus()
}
}
class EmployeeType {
static newType (type) {
switch (type) {
case EmployeeType.ENGINEER:
return new Engineer()
case EmployeeType.SALESMAN:
return new Salesman()
case EmployeeType.MANAGER:
return new Manager()
}
}
}
class Employee {
payAmount () {
return this.state.payAmount(this)
}
getType () {
return this.state
}
setType (type) {
this.state = EmployeeType.newType(type)
}
}
Null 검사를 Null 객체에 위임(Introduce Null Object)
null 값을 검사하는 코드가 계속 나올 땐 null 값을 널 객체로 만들자
// Before
class Customer { }
class Site {
getCustomer () {
return this.customer
}
setCustomer () {
this.customer = new Customer()
}
}
const customer = new Site().getCustomer()
if (customer === null) plan = BillingPlan.basic()
else plan = customer.getPlan()
if (customer === null) customerName = "occupant"
else customerName = customer.getName()
// After
class NullCustomer {
isNull () {
return true
}
}
class Customer {
isNull () {
return false
}
}
class Site {
getCustomer () {
return this.customer === null ?
new NullCustomer() :
this.customer
}
setCustomer () {
this.customer = new Customer()
}
}
const customer = new Site().getCustomer()
if (customer.isNull()) plan = BillingPlan.basic()
else plan = customer.getPlan()
if (customer.isNull()) customerName = "occupant"
else customerName = customer.getName()
어설션 넣기(Introduce Assertion)
일부 코드가 프로그램의 어떤 상태를 전제할 뗀, 어설션을 넣어서 그 전제를 확실하게 코드로 작성하자
어설션
: 항상 참으로 전제되는 조건문으로 실패하면 프로그래머가 오류를 범한 것으로 예외 통지를 하게 된다.
// Before
const getExpenseLimit = () => {
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit :
primaryProject.getMemberExpenseLimit()
}
// After
class Assert {
static isTrue (val) {
if (val !== true) throw 'Error'
}
}
const getExpenseLimit = () => {
Assert.isTrue(expenseLimit !== NULL_EXPENSE || primaryProject !== null)
return (expenseLimit != NULL_EXPENSE) ?
expenseLimit :
primaryProject.getMemberExpenseLimit()
}