티스토리 뷰
첫번째 카탈로그인 6장에는 가장 기본적이고 많이 사용하는 리팩터링들로 구성되어 있다.
- 가장 많이 사용하는 리팩터링
- 6.1 함수 추출하기
- 6.3 변수 추출하기
- 반대로 진행하는 리팩터링
- 6.2 함수 인라인하기
- 6.4 변수 인라인하기
- 함수와 변수 최적화
- 6.5 함수 선언 바꾸기
- 6.6 변수 캡슐화하기
- 6.7 변수 이름 바꾸기
- 6.8 매개변수 객체 만들기
- 함수를 고수준 모듈로 묶기
- 6.9 여러 함수를 클래스로 묶기
- 6.10 여러 함수를 변환 함수로 묶기
- 모듈의 처리 과정을 명확한 단계로 구분 짓기
- 6.11 단계 쪼개기
6.1 함수 추출하기 (Extract Function)
개요
- 가장 많이 사용하는 리팩터링
- 코드가 하는일을 파악한 다음, 함수로 추출하고 목적에 맞는 이름 붙이는 작업이다
적용 시점
- 코드가 무슨 일을 하는지 파악하기 어려운 경우
- 함수가 길어질 때, 재사용성이 필요할 때
효과
- 코드를 읽을 때 함수의 목적이 쉽게 파악되고 본문 코드에 대해서는 신경쓰지 않아도 됨
사례
- 스몰토크 그래픽스 클래스의 highlight() 메서드
- 색상을 반전시켜 강조하는 highlight() 메서드의 경우 본문에 reverse() 메서드 호출 1라인만 존재함
- highlight() 메서드가 존재하는 이유는 코드의 목적(강조)과 구현(반전) 사이의 차이가 크기 때문
성능 최적화 지침
- 함수 호출이 많아져서 성능이 느려질까 걱정하는 경우도 있는데 그럴 일은 흔하지 않다
- 성능 최적화에 대해서는 항상 일반 지침을 따르도록 한다
- 최적화를 할 때는 다음 두 규칙을 따르기 바란다. 첫 번째, 하지 마라. 두 번째(전문가 한정), 아직 하지 마라 - M. A. 잭슨
before
function printOwing(invoice) {
printBanner();
let outstanding = calculateOutstanding();
//print details
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
after
function printOwing(invoice) {
printBanner();
let outstanding = calculateOutstanding();
printDetails(outstanding);
function printDetails(outstanding) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
}
6.2 함수 인라인하기 (Inline Function)
개요
- 책에서는 목적이 드러나는 이름의 짧은 함수 사용을 권하지만 때로는 함수 본문이 이름만큼 명확한 경우도 있음
- 이럴 때는 쓸데없는 간접 호출 함수이기 때문에 제거하는게 유리함
적용 시점
- 함수 본문이 이름만큼 명확한 경우
- 간접 호출을 너무 과하게 쓰는 코드 (단순히 위임하기만 하는 함수들이 많아 복잡하게 얽힌 경우)
효과
- 유용한 것만 남기고 불필요한 메서드를 제거할 수 있다
before
function getRating(driver) {
return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}
function moreThanFiveLateDeliveries(driver) {
return driver.numberOfLateDeliveries > 5;
}
after
function getRating(driver) {
return (driver.numberOfLateDeliveries > 5) ? 2 : 1;
}
6.3 변수 추출하기 (Extract Variable)
개요
- 과도하게 복잡한 표현식은 코드를 이해하기 어렵게 함
- 변수를 활용하면 복잡한 로직을 구성하는 단계마다 이름을 붙여 코드의 목적을 명확하게 드러낼 수 있음
- 단, 변수가 함수를 벗어난 넓은 문맥에서까지 사용된다면 주로 함수로 추출
적용 시점
- 표현식이 복잡해서 이해하기 어려울 때
효과
- 코드의 목적을 명확하게 드러낼 수 있다
- 디버깅에도 도움이 된다
before
return order.quantity * order.itemPrice -
Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +
Math.min(order.quantity * order.itemPrice * 0.1, 100);
after
const basePrice = order.quantity * order.itemPrice;
const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemPrice * 0.05;
const shipping = Math.min(basePrice * 0.1, 100);
return basePrice - quantityDiscount + shipping;
6.4 변수 인라인하기 (Inline Variable)
개요
- 변수는 함수 안에서 표현식을 가리키는 이름으로 쓰이며, 대체로 긍정적임
- 하지만, 불필요할 때는 변수를 인라인 하는 것이 필요
적용 시점
- 변수의 이름이 원래 표현식과 다를 바 없을 때
효과
- 불필요한 변수를 제거할 수 있고 리팩터링 방해 요소를 줄인다
before
let basePrice = anOrder.basePrice;
return (basePrice > 1000);
after
return anOrder.basePrice > 1000;
6.5 함수 선언 바꾸기 (Change Function Declaration)
개요
- 함수명 변경, 파라미터 추가/삭제 모두 포함
- 함수는 프로그램을 작게 나눈 구성 요소로 소프트웨어를 조립하는 연결부 역할을 함
- 가장 중요한 요소는 함수의 이름으로 주석으로 함수의 목적을 설명해보면 좋은 이름을 떠올릴 수 있음
- 매개변수는 함수가 외부 세계와 어우러지는 방식을 정의하며, 매개변수를 올바르게 선택하기란 단순히 규칙 몇개로 표현할 수 없음 (높은 결합도 vs 낮은 결합도)
적용 시점
- 함수 이름 : 의미에 와 닿지 않는 이름을 발견하는 경우
- 매개변수 : 정답이 없음. 외부 인터페이스와의 결합도가 필요한지 문맥을 판단 후 결정
효과
- 좋은 이름은 함수의 호출문만 보고도 무슨 일을 하는지 파악할 수 있다
- 함수들을 조합하여 프로그램을 만들기 쉬워진다
before
function circum(radius) {...}
after
function circumference(radius) {...}
6.6 변수 캡슐화 하기 (Encapsulate Variable)
개요
- 변수는 참조하는 모든 부분을 한 번에 바꿔야만 제대로 동작하기 때문에 함수에 비해 다루기가 까다로움
- 짧은 함수의 임시 변수처럼 유효범위가 좁은 경우 어렵지 않지만, 유효범위가 넓어질수록 다루기 어려워짐 (전역데이터가 골칫거리인 이유)
적용 시점
- 유효범위가 넓은 변수인 경우
- 레거시 코드를 다룰 때
- 이런 변수를 참조하는 코드를 추가하거나 변경할 때마다 최대한 캡슐화 한다
- 단, 불변 데이터는 수정이 불가하기 때문에 가변 데이터보다 캡슐화할 이유가 적다
효과
- 사전 캡슐화를 통해 데이터의 재구성이라는 어려운 작업을 함수 재구성이라는 더 단순한 작업으로 변환할 수 있다
- 데이터를 변경하고 사용하는 코드에 대한 전/후 검증 및 처리 로직을 쉽게 끼워 넣을 수 있다
레거시 코드란?
- legacy 의 사전적 정의
- 1.(죽은 사람이 남긴) 유산
- 2.(과거의) 유산
- 최초 개발자가 유지 관리 하지 않는 코드
- 테스트가 불가능하거나 어려운 코드
before
let defaultOwner = {firstName: "Martin", lastName: "Fowler"};
after
let defaultOwnerData = {firstName: "Martin", lastName: "Fowler"};
export function defaultOwner() {return defaultOwnerData;}
export function setDefaultOwner(arg) {defaultOwnerData = arg;}
6.7 변수 이름 바꾸기 (Rename Variable)
개요
- 변수는 프로그래머가 하려는 일에 관해 많은 것을 설명해주나 이는 이름을 잘 지었을 때만 해당함
- 이름을 잘못 짓는 경우
- 고민을 충분히 하지 않아서
- 개발을 더 하다 보니 문제에 대한 이해도가 높여저서
- 사용자의 요구가 달라져서 프로그램의 목적이 변해서
적용 시점
- 변수 이름이 목적과 다른 경우, 명확하지 않은 경우
- 사용 범위가 큰 변수일수록 이름의 중요함
- 영속적인 필드 VS 람다식의 변수
효과
- 변수명만으로 코드가 하는 일을 파악하기 쉬워진다
before
let a = height * width;
after
let area = height * width;
6.8 매개변수 객체 만들기 (Introduce Parameter Object)
개요
- 데이터가 여러 함수로 함께 몰려다니는 경우 여러 데이터 구조 하나로 모아줄 필요가 있다
적용 시점
- 데이터 항목 여러개가 여러 함수로 함께 몰려다니는 패턴이 있는 경우
효과
- 데이터 사이의 관계가 명확해진다
- 매개변수 수가 줄어들고 관련 함수들의 일관성도 높여 준다
- 궁극적으로 코드를 더 근본적으로 바꿔 줄 수 있는 발판을 마련해준다
- 데이터 구조를 활용하는 형태로 프로그램 동작을 재구성하여 문제 영역을 훨씬 간결하게 표현하는 새로운 추상적 개념으로 격상된다
before
function amountInvoiced(startDate, endDate) {...}
function amountReceived(startDate, endDate) {...}
function amountOverdue(startDate, endDate) {...}
after
function amountInvoiced(aDateRange) {...}
function amountReceived(aDateRange) {...}
function amountOverdue(aDateRange) {...}
6.9 여러 함수를 클래스로 묶기 (Combine Functions into Class)
개요
- 공통 데이터를 중심으로 작동하는 함수들을 클래스로 묶는 작업
- 여러 함수를 변환 함수로 묶기6.10절 방법도 있으나 어느 방식으로 진행할지는 문맥을 살펴보고 정함
적용 시점
- 공통 데이터를 중심으로 긴밀하게 작동하는 함수들이 발견되는 경우
효과
- 함수들이 공유하는 공통 환경을 명확하게 표현할 수 있다
- 각 함수에 전달되는 인수를 줄여서 객체 내부에서의 함수 호출을 간결하게 만들 수 있다
before
function base(aReading) {...}
function taxableCharge(aReading) {...}
function calculateBaseCharge(aReading) {...}
after
class Reading {
base() {...}
taxableCharge() {...}
calculateBaseCharge() {...}
}
6.10 여러 함수를 변환 함수로 묶기 (Combine Functions into Transform)
개요
- 데이터를 입력받아 여러 가지 정보를 도출하는 로직을 한데로 모아두는 작업
- 변환 함수는 원본 데이터를 입력받아서 필요한 정보를 모두 도출한 뒤, 각각을 데이터의 필드에 넣어 반환하는 방식
적용 시점
- 데이터를 입력받아 여러 가지 정보를 도출하는 로직이 분산된 경우
- 이 리팩터링 대신 여러 함수를 클래스로 묶기6.9절 로 처리해도 되며 둘 중 어느 것을 적용해도 좋음
- 대체로 이미 반영된 프로그래밍 스타일을 따름
- 단, 원본 데이터를 갱신하는 내용이 있을때는 함수를 클래스로 묶기
효과
- 검색과 갱신을 일관된 장소에서 처리할 수 있고 로직 중복도 막을 수 있다
- 도출 과정을 검토할 일이 생겼을 때 변환 함수만 살펴보면 된다
before
function base(aReading) {...}
function taxableCharge(aReading) {...}
after
function enrichReading(argReading) {
const aReading = _.cloneDeep(argReading);
aReading.baseCharge = base(aReading);
aReading.taxableCharge = taxableCharge(aReading);
return aReading;
}
6.11 단계 쪼개기 (Split Phase)
개요
- 코드를 수정해야 할 때 두 대상을 동시에 생각할 필요 없이 하나에만 집중하기 위해 별개 모듈로 나누는 작업을 진행
- 단순히 동작을 연이은 두 단계로 쪼개거나 필요 시 입력값을 다루기 편한 형태로 먼저 가공한다
적용 시점
- 여러 단계로 분리하면 좋을만한 코드를 발견할 때
효과
- 하나의 목적에만 집중할 수 있어 코드 수정이 쉬워짐
before - 상품의 결제 금액을 계산하는 코드
function priceOrder(product, quantity, shoppingMethod) {
const basePrice = product.basePrice * quantity;
const discount = Math.max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
const shippingPerCase = (basePrice > shippingMethod.discountThreshold) ? shippingMethod.discountedFee : shippingMethod.feePerCase;
const shippingCost = quantity * shippingPerCase;
const price = basePrice - discount + shippingCost;
return price;
}
after - 상품 가격과 배송비 계산을 두 단계로 나눔
function priceOrder(product, quantity, shoppingMethod) {
const priceData = calculatePricingData(product, quantity);
return applyShipping(priceData, shippingMethod);
}
// 중간 데이터
function calculatePricingData(product, quantity) {
const basePrice = product.basePrice * quantity;
const discount = Math.max(quantity - product.discountThreshold, 0) * product.basePrice * product.discountRate;
return {basePrice: basePrice, quantity: quantity, discount: discount};
}
function applyShipping(priceData, shippingMethod) {
const shippingPerCase = (priceData.basePrice) > shippingMethod.discountThreshold) ? shippingMethod.discountedFee : shippingMethod.feePerCase;
const shippingCost = priceData.quantity * shippingPerCase;
return priceData.basePrice - priceData.discount + shippingCost;
}
참고
http://www.yes24.com/Product/Goods/89649360
https://refactoring.com/catalog
'소프트웨어공학, CS > 리팩터링 2판' 카테고리의 다른 글
[리팩터링 2판] 11장 API 리팩터링 (0) | 2021.05.16 |
---|---|
[리팩터링 2판] 10장 조건부 로직 간소화 (0) | 2021.04.18 |
[리팩터링 2판] 9장 데이터 조직화 (0) | 2021.04.04 |
[리팩터링 2판] 8장 기능 이동 (2) | 2021.03.28 |
[리팩터링 2판] 7장 캡슐화 (0) | 2021.03.13 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 그림으로 배우는 HTTP & Network
- 매개변수화
- 리팩토링
- https
- 조건부 로직
- 안심 첫 문장
- 질의함수
- Refactoring
- aws fargate
- 제어플래그
- 위임
- Debug It! 실용주의 디버깅
- 디버깅
- 박소연
- 코드스멜
- 마틴파울러
- AWS
- 코드악취
- 변경함수
- 지시의 언어
- SSL
- 일 잘하는 사람은 단순하게 말합니다
- 리팩터링이란
- HTTP
- amazon aurora
- 일잘러
- Debugging
- Debug
- amazon vpc
- 그림으로 공부하는 IT 인프라 구조
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함