템플릿 메서드 패턴 예제 2 - Authenticator (Local, SNS)
in Development on Design Pattern
리팩토링 전
public class LocalAuthenticator {
public Auth authenticate(String id, String pw) {
// 사용자 정보로 인증 확인
User user = userDao.selectById(id);
boolean auth = user.equalPassword(pw);
// 인증 실패 시 예외 발생
if (!auth) {
throw new AuthException();
}
// 인증 성공 시, 인증 정보 제공
return new Auth(id, user.getName);
}
}
public class SnsAuthenticator {
public Auth authenticate(String id, String pw) {
// 사용자 정보로 인증 확인
boolean auth = snsClient.authenticate(id, pw);
// 인증 실패 시 예외 발생
if (!auth) {
throw new AuthException();
}
// 인증 성공 시, 인증 정보 제공
return new Auth(id, snsClient.find(id));
}
}
템플릿 메서드 패턴 적용
Step 1) 로직이 ‘다른 부분’과 ‘동일한 부분을 각각 메서드로 추출
public class LocalAuthenticator {
public Auth authenticate(String id, String pw) {
// 사용자 정보로 인증 확인
~~User user = userDao.selectById(id);
boolean auth = user.equalPassword(pw);~~
boolean auth = **doAuthenticate(id, pw);**
// 인증 실패 시 예외 발생
if (!auth) {
throw new AuthException();
}
// 인증 성공 시, 인증 정보 제공
~~return new Auth(id, user.getName);~~
**return createAuth(id);**
}
**private boolean doAuthenticate(String id, String pw) {**
User user = userDao.selectById(id);
return user.equalPassword(pw);
**}
private Auth createAuth(String id) {
User user = userDao.selectById(id);
return new Auth(id, user.getName);
}**
}
public class SnsAuthenticator {
public Auth authenticate(String id, String pw) {
// 사용자 정보로 인증 확인
~~boolean auth = snsClient.authenticate(id, pw);~~
boolean auth = **doAuthenticate(id, pw);**
// 인증 실패 시 예외 발생
if (!auth) {
throw new AuthException();
}
// 인증 성공 시, 인증 정보 제공
~~return new Auth(id, snsClient.find(id));~~
return **createAuth(id);**
}
**private boolean doAuthenticate(String id, String pw) {**
return snsClient.authenticate(id, pw);
**}
private Auth createAuth(String id) {
return snsClient.authenticate(id, pw);
}**
}
Step2) 중복된 로직을 합치기 위해 추상화한 부모 클래스 생성
public abstract class Authenticator {
**// 템플릿 메서드 (흐름 제어를 갖고 있는 메서드)**
public Auth authenticate(String id, String pw) {
// 사용자 정보로 인증 확인
boolean auth = **doAuthenticate(id, pw);**
// 인증 실패 시 예외 발생 (그대로)
if (!auth) {
throw new AuthException();
}
// 인증 성공 시, 인증 정보 제공
return **createAuth(id);**
}
**abstract boolean doAuthenticate(String id, String pw);
abstract Auth createAuth(String id);**
}
Step3) 부모 클래스에 맞게 자식 클래스 구현
- 각 자식 클래스들은 부모 클래스를 상속 받아, 추상 메서드로 분리한 부분을
@Override
를 통해 각각의 로직을 작성하면 된다.
public class LocalAuthenticator extends Authenticator {
public Auth authenticate(String id, String pw) {
@Override
boolean doAuthenticate(String id, String pw) {
User user = userDao.selectById(id);
return user.equalPassword(pw);
}
@Override
Auth createAuth(String id) {
User user = userDao.selectById(id);
return new Auth(id, user.getName(id));
}
}
}
public class SnsAuthenticator extends Authenticator {
public Auth authenticate(String id, String pw) {
@Override
boolean doAuthenticate(String id, String pw) {
boolean auth = snsClient.authenticate(id, pw);
return auth;
}
@Override
Auth createAuth(String id) {
return new Auth(id, snsClient.find(id));
}
}
}
References
- 한 번 읽으면 두 번 깨닫는 객체지향 프로그래밍 (김동헌, e비즈북스)
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 (최범균, 인투북스)
- Head First Design Pattern (에린 프리먼, 한빛미디어)