Closed rkaehdaos closed 2 years ago
이 함수의 표현은 객체지향의 메서드, 절차형의 프로시저/서브루틴과 매칭된다
배경
- 코드 조각을 찾아 무슨 일을 하는지 파악한 후 독립 함수로 추출하고 목적에 맞는 네이밍
- 언제 필요?
- 길이?? : 예를 들어 한 화면을 넘어가면 안되다는 규칙을 떠올릴 수 있음
- 재사용성?? : 2번 이상 사용될 코드?
- 책에서 말하는 가장 합리적인 기준? "목적과 구현을 분리"하는 방식
- 코드가 무슨 일을 하는지 파악하는데 한참 걸린다? → 해당 부분을 함수 추출 후 '무슨 일'에 걸맞는 네이밍 작업
- 나중에 읽을 때 목적이 한 눈에 들어오고. 함수 내부 본문 코드는 더 신경 쓸 필요가 없음
- 마틴 파울러 경험 이야기
- 경험상 대여섯줄 넘어가면 냄새가 난다. 단 한 줄 짜리 함수를 만드는 일도 적지 않다
- 켄트백의 극단적인 스몰토크 예
helight()
는 단순히 색상반전을 위한reserve()
호출- 메서드 이름이 구현 코드보다 길었음
- 코드의 목적(강조)와 구현(반전)사이의 차가 그만큼 컸으므로 문제가 되지 않는다
- 함수 호출 많아져 성능 느려짐?
- 요즘엔 그럴 일이 거의 없음
- 짧으면 더 캐싱이 쉽다 → 컴파일러가 최적화시 유리 성능 최적화 일반 원칙 : 첫번째 하지마라, 두 번째(전문가 한정), 아직 하지 마라 - M. A. Jackson
다음 고려점 : 임시 변수의 스코프
추출된 코드안에서만 사용되는 경우 → 초기화 시점과 사용되는 지점이 떨어져 있다면 문장 슬라이드하기를 활용해서 변수 조작을 모두 한 곳에서 처리하도록
문장 슬라이드 :
변수가 추출된 함수 밖에서 사용될 경우 → 변수에 대입된 새 값을 반환해야 한다.
이 절차를 적용하기 어려울정도로 복잡하다면 인라인하기를 적용하면 안되는 케이스
//ex2
class Order{
constructor(aRecord) {
this._data = aRecord;
}
get quantity() {return this._data.quantity;}
get itemPrice() {return this._data.itemPrice;}
get price() {
return order.quantity * order.itemPrice -
Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +
Math.min(order.quantity * order.itemPrice * 0.1, 100);
}
}
시그니처 바꾸기
효과적인 방법 : 주석을 이용해 함수 목적을 설명해보자, 그러면 주석이 멋진 네이밍으로 되돌아올 때가 있다
변경할게 둘 이상?
→ 호출문과 선언문을(다형성 구현의 경우 여러 선언문 모두를) 한번에 수정해야 한다
→ 많지 않거나 좋은 도구를 쓰면 그리 어렵지 않으나 아주 많다면 힘들어짐
→ 또한 같은 이름의 메서드가 여러 클래스에 정의되어있을대가 문제
→ 예컨데 changeAddress()
가 사람 클래스와 계약 클래스 모두에 정의된 경우
→ 하나만 바꾸고 싶을 때 난감함
→이럴 때 마이그레이션 절차대로 해본다
circumference()
를 만들고나서 잠시 리팩터링 멈춤circum()
에 deprecated를 표시circum()
의 클라이언트들 모두가 circumference()
를 사용하게 바뀔 때까지 기다린다circum()
을 삭제한다필드 캡슐화
함수는 데이터보다 다루기 수월하다
데이터는 함수보다 다루기 어렵다 : 이런식으로 처리가 안되므로
따라서 접근할수 있는 스코프가 넓은 데이터를 옮길때는?
데이터 캡슐화의 다른 장점들
객체 지향에서 객체의 데이터를 항상 private로 유지해야 한다는 이야기가 바로 여기!
불변 데이터는 캡슐화 이유가 적다
변수 뿐 아니라 변수에 담긴 내용을 변경하는 행위도 제어할 수 있도록 캡슐화를 원한다면?
그 값을 바꾸지 못하게 만든다
export function defaultOwner() {return Object.assign({}m defaulutOwnerData);}
아니면 아예 변경할 수 없도록 만든다 [레코드 캡슐화 하기]
setter에서도 복제본을 만드는 편이 좋은지도 살펴보자
명확한 프로그래밍의 핵심은 이름 짓기
함수 호출 한 번으로 끝나지 않고 갑싱 영속되는 필드라면 ? 아주 신중하게 네이밍!
가장 간단한 예
상수이름 바꾸기
범위 개념은 객체 하나로 묶어 표현하는게 나은 대표적인 예다
호출코드
operatingPlan은 범위의 시작과 끝을 readingsOutsideRange와 다르게 표현하고 있다
범위(Range)s는 객체 하나로 묶어서 표현하는 것이 나은 대표적인 예시
클래스
공동테이터(흔히 호출시 인수로 전달되는)를 중심으로 긴밀히 엮여 작동하는 함수무리는 클래스 하나로
함수로 한데 묶는 다른 방법 존재
클래스 묶을시 장점
중첩함수로 묶기
클래스를 지원하지 않는 언어?
도출 정보
변환함수
클래스 묶기와 중요한 차이
함수 추출하기로 같은 효과?
분리의 가장 간편한 방법 : 한 스텝을 연이은 스텝 2개로 쪼개기
SW 규모에 상관없이 여러 스텝으로 분리하면 좋을때마다 단계 쪼개기 해보자
shippingMethod 1 step에서 사용하지 않으므로 그대로 둔다
quantity
discount
명령어 인수를 담은 문자열 배열을 2step에 적합한 인터페이스로 바꿔주는 변환기(transformer)객체를 만들어도 된다
private static class CommandLine {
String[] args;
public CommandLine(String[] args) {
this.args = args;
if (args.length == 0) throw new RuntimeException("파일명을 입력하세요");
}
String filename(){
return args[args.length - 1];
}
boolean onlyCountReady(){
return Stream.of(args).anyMatch(arg -> "-r".equals(arg));
}
}
}
Intro
저수준 -> 고수준