이 글은 최범균님의 Inflearn 강의를 학습한 내용을 정리하였습니다.
public AuthResult authenticate(String id, String pw) {
Member mem = findOne(id);
if (mem == null) return AuthResult.NO_MATCH;
if (mem.getVerificationEmailStatus() != 2) {
return AuthResult.NO_EMAIL_VERIFIED;
}
if (passwordEncoder.isPasswordVaild(mem.getPassword(), pw, mem.getId())) {
return AuthResult.SUCCESS;
}
return AuthResult.NO_MATCH;
}
위 코드는 인증과 관련된 코드로 아이디와 암호를 매개변수로 받아
한다.
예제 코드를 캡슐화하기 위해 적용할 수 있는 규칙은 Tell, Don't Ask (데이터를 달라하지 말고 해달라고 하기)다.
아래의 조건문을 보면
if (mem.getVerificationEmailStatus() != 2) { ... }
맴버 객체에게 verificationEmailStatus 값을 받아서 2가 아닌지 판단하고 있다.
값을 받지말고 연산한 결과를 받아오도록 수정하면 아래와 같다.
이와같이 캡슐화를 수행하면 나중에 verificationEmailStatus가 3일 경우 인증된 상태라고 바뀌어도 내부 구현만 수정하고 외부의 코드는 수정하지 않아도 된다.
//Movie.java
public class Movie {
public static int REGULAR = 0;
public static int NEW_RELEASE = 1;
private int priceCode;
public int getPriceCode() {
return priceCode;
}
...
}
//Rental.java
public class Rental {
private Movie movie;
private int daysRented;
public int getFrequentRenterPoints() {
if (movie.getPriceCode() == Movie.NEW_RELEASE && daysRented > 1)
return 2;
else
return 1;
}
...
}
출처: 리팩토링(마틴 파울러 저)
2.Rental.java
영화 대여를 담당하는 객체이다.
최신 영화를 하루 이상 대여했다면 2포인트를 주고 그렇지 않다면 1포인트를 리턴한다.
이 예제 역시 데이터를 달라고 하는 부분을 캡슐화할 수 있다.
아래의 코드를 Movie 객체에게 부탁하자.
movie.getPriceCode() == Movie.NEW_RELEASE
결과는 아래와 같다.
하지만 이 코드 역시 뭔가 아쉽다.
조건 전체를 Movie 객체에 맡겨보자.
movie.isNewRelease() && daysRented > 1
최종 결과는 아래와 같다.
이렇게 캡슐화를 하면
Movie 객체의 getFrequentRenterPoints의 코드만 변경하면 된다.
캡슐화 예제2는 데이터를 들고 있는 객체가 기능을 제공하면서 해당 기능에 필요한 다른 값을 파라미터로 받는 경우이다.
//Timer.java
public class Timer {
public long startTime;
public long stopTime;
}
//실행 시간 측정 코드
Timer t = new Timer();
t.startTime = System.currentTimeMillis(); //(1)
...
t.stopTime = System.currentTimeMillis(); //(2)
long elaspedTime = t.stopTime - t.startTime; //(3)
Timer.java
startTime과 stopTime의 데이터를 가지고 있다.
실행 시간 측정 코드
타이머 데이터 클래스에 시작 시간과 종료 시간을 저장하고 로직의 실행 시간을 구하는 코드이다.
위 코드는 Timer 데이터 클래스를 가져다 사용하는 절차 지향적으로 작성되어있다.
캡슐화를 적용하기 전에 주석이 있는 코드(1), (2), (3)의 기능을 알아보자.
각 기능을 Timer 객체로 묶어주면 코드는 아래와 같다.
기존 코드에서 데이터 클래스로 사용되던 Timer가 기능을 제공하는 객체로 바뀌었다.
Timer를 캡슐화함으로써 측정 시간 단위를 millisecond에서 nanosecond로 변경하더라도 외부의 코드는 변경하지 않고 내부의 구현을 변경하면된다.
public void verifyEmail(String token) {
Member mem = findByToken(token);
if (mem == null) throw new BadTokenException();
if (mem.getVerificationEmailStatus() == 2) {
throw new AlreadyVerifiedException();
} else {
mem.setVerificationEmailStatus(2);
}
// .. 수정사항 DB 반영
}
연습1, 2, 3 과정과 비슷하게 객체에 맡기고 싶은 부분은
mem.getVerificationEmailStatus() == 2
mem.setVerificationEmailStatus(2)
조건을 검사하는 코드(1)와 데이터를 변경하는 코드(2) 이렇게 두 부분이다.
1번을 캡슐화한 코드는 아래와 같다.
조건 검사를 캡슐화했지만 코드의 구조는 개선되지 않았다.
이 코드의 구조는 데이터를 가져와서 판단하고 결과에 따라서 데이터를 바꾼다.
이러한 코드는 조건문을 통으로 캡슐화했을 때 개선될 가능성이 높아진다.
개선한 코드는 아래와 같다.