11. API 리팩터링

11.1 질의 함수와 변경 함수 분리하기#

// before
public long getTotalOutstandingAndSendBill() {
long result = customer.getInvoices()
.stream()
.mapToLong(Invoice::getAmount)
.sum();
emailGateway.send(formatBill(customer));
return result
}
// after
public long getTotalOutstanding() {
return customer.getInvoices()
.stream()
.mapToLong(Invoice::getAmount)
.sum();
}
public void sendBill() {
emailGateway.send(formatBill(customer));
}
  • 겉보기 부수효과(observable side effect)가 전혀 없이 값을 반환해주는 함수를 추구해야 함
    • 겉보기 부수효과: 데이터 값이 변경되는 것, 외부로 입출력이 있는 것
    • 언제 어디서든 호출되도 문제가 없음
    • 명령-질의 분리(command-query separation) 라고도 함

11.2 매개 변수화하기#

// before
public void raiseSalaryTenPercent() {
this.salary = this.salary.multiply(1.1);
}
public void raiseSalaryFivePercent() {
this.salary = this.salary.multiply(1.05);
}
// after
public void raiseSalary(double factor) {
this.salary = this.salary.multiply(factor);
}
  • 리터럴 값만 다른 함수가 있다면 매개변수화 해서 함수의 재사용성을 늘리는 전략

11.3 플래그 인수 제거하기#

// before
public void setDimension(String name, double value) {
if (name.equals("height")) {
this.height = value;
return;
}
if (name.equals("width")) {
this.width = value;
return;
}
}
// after
public void setHeight(double value) {
this.height = value;
}
public void setWidth(double value) {
this.width = value;
}
  • 인자에 boolean, enum, string이 들어가면 의심해 볼 수 있음
  • 플래그 인수는 호출 할 수 있는 함수가 무엇이고 어떻게 해야하는지 파악이 어려워짐
  • 플래그 인수가 둘 이상이면 함수 하나가 너무 많은 일을 하고 있다는 신호

11.4 객체 통째로 넘기기#

// before
double low = room.getDaysTempRange().getLow();
double high = room.getDaysTempRange().getHigh();
if (plain.withinRange(low, high))
// after
if (plain.withinRange(room.getDaysTempRange()))
  • 객체를 통째로 넘기면 좋은점
    • 변화에 대응하기 쉬움 (함수에 인자가 더 필요하면 객체에 담을 수 있음)
    • 가독성 증가
    • 로직 중복 제거
  • 함수가 레코드(객체)에 의존하기를 원치 않을땐 X
  • 객체의 일부를 꺼내 그것으로 무엇을 하는 로직이 있다면, 이는 해당 객체에 메소드화 할 수 있다는 신호

11.5 매개변수를 질의 함수로 바꾸기#

// before
availableVacation(employee, employee.getGrade());
public boolean availableVacation(Employee employee, Grade grade) {...}
// after
availableVacation(employee);
public boolean availableVacation(Employee employee) {
Grade grade = employee.getGrade(); // 매개변수를 질의함수로 바꾼 부분
...
}
  • 매개변수 목록은
    • 중복을 피하는게 좋음
    • 짧을수록 좋음
  • 저자는 호출하는 쪽을 간소화하고, 피호출 함수에 책임을 지게 만드는 구조를 주로 다룸
    • 물론 해당 클래스가 그정도의 책임을 가져도 될 경우에만!
    • 그래서 해당 리팩터링 시, 피호출자(리팩터링 대상)의 클래스가 과도한 의존성을 지니게 된다면 주의해야 함
  • 리팩터링 대상 함수는 참조 투명해야 함
    • 참조 투명: 똑같은 값에 항상 같은 결과 -> 해당 함수가 가변 전역 변수를 이용하는 일이 없어야 함

11.6 질의 함수를 매개변수로 바꾸기#

  • 11.5 와 반대되는 리팩터링
  • 클래스 설계 시 책임의 소재에 따라 달라질수 있는 문제
  • 매개변수 객체가 참조 투명하지 않다면 해당 함수도 참조 투명하지 않음
    • 이때 해당 리팩터링 적용 가능
  • 단점은 호출자가 매개변수로 어떤 값을 보내야 할 지 알아야 함
    • 결국 설계실력을 닦아야 하는 문제로 귀결
  • 자바스크립트, 파이썬 등의 일부 언어에서는 객체 안에 데이터를 직접 얻어 낼 수 있는 방법이 항상 존재 하지만,
    • 해당 클래스는 불변용으로 설계한 것을 알리는 것만으로도 좋은 효과를 누릴 수 있음

11.7 세터 제거하기#

  • 외부에 세터를 제공하지 않음으로 해당 필드는 불변이라는 의도를 알릴 수 있음
  • 객체 생성 후 수정되지 않길 원하는 필드라면 세터 제공 X
  • 세터 제거하기 리팩터링이 필요한 2가지 상황
    • 무조건 접근자 메서드를 통해 필드를 다루려 할 때 (생성자에서 세터..)
    • 클라이언트에서 생성 스크립트를 사용해 객체를 생성할 때
      • 생성 스크립트: 생성자로 객체를 생성하고 일련의 세터로 객체를 완성하는 형태

11.8 생성자를 팩터리 함수로 바꾸기#

  • 생서자의 한계
    • 자바 생성자는 그 생성자를 정의한 클래스의 인스턴스만 반환 가능 (하위, 프록시 X)
    • 기본 이름보다 더 적절한 이름 불가
    • 일반 함수가 오길 기대하는 자리에 쓸 수 없음
  • 팩터리 함수는 이런 제약이 없음 -> 원하는 적잘한 다른 무언가로 대체 가능

11.9 함수를 명령으로 바꾸기#

// before
public int score(Person candidate, Exam medicalExam) {
int result = 0;
int healthLevel = 0;
// 이하 생략
}
// after
public class Scorer {
private Person candidate;
private Exam medicalExam;
private int result;
private int healthLevel;
public Scorer(Person candidate, Exam medicalExam) {
this.candidate = candidate;
this.medicalExam = medicalExam;
}
public void execute() {
this.result = 0;
this.healthLevel = 0;
}
}
  • 함수를 클래스화 하여 메세지로 수행할 수 있게 함
  • 이런 클래스를 명령(command) (객체) 라고 함
    • 명령 객체는 대부분 메서드 하나로 구성됨, 요청해 실행하는 것이 이 객체의 목적
    • undo 보조 연산, 현재 수명주기를 정밀하게 만들어주는 메서드 제공, 상속과 훅 사용을 이용해 맞춤형 제공 등 다양한 기능을 제공
  • 유연성은 언제나 복잡성을 키우고 얻는 대가니 잘 판단

11.10 명령을 함수로 바꾸기#

  • 11.9 와 반대
  • 로직이 크게 복잡하지 않다면 명령 객체를 굳이 만들 필요 없음

11.11 수정된 값 반환하기#

  • 함수 내에서 전역 변수나, 아규먼트의 객체를 수정하지 않고, 새로운 값으로 반환
  • 함수 내에서 수정할 경우 변경의 추적이 어려움

11.12 오류 코드를 예외로 바꾸기#

  • 오류 코드 검사는 매번 검사해줘야하는 불편함이 있음
  • 예외는 상위로 던지거나 하는 독자적인 매커니즘을 사용하여 다른 객체에 오류를 핸들링 하는 역할을 넘길 수 있음
  • 예외는 정확히 예상 밖의 동작일 때만 쓰여야 함
  • 검사 예외, 비검사 예외 잘 구분해서 사용해야겠다

11.13 예외를 사전확인으로 바꾸기#

  • 예외는 뜻밖의 오류, 즉 말그대로 예외적으로 동작할 때만 쓰여야 함
  • 예외를 던지기 전, 함수 호출 전에 조건을 검사할 수 있다면 조건 검사로 대체하자
Last updated on