위와 같이 암호 변경이라는 기능은 여러 하위 기능으로 분해될 수 있다.
이처럼 각각의 하위 기능들을 책임을 가질 클래스에 알맞게 분배해야한다.
public class ChangePasswordService {
public Result changePassword(String id, String oldPw, String newPw) {
Member mem = memeberRepository.findOne(id);
if(mem == null){
return Result.NO_MEMBER;
}
try {
mem.changePassword(oldPw, newPw);
return Result.SUCCESS;
} catch(BadPasswordException ex){
return Result.BAD_PASSWORD;
}
}
}
위 코드를 보면, MemebeRepository
, Member
가 각각의 기능을 제공하고 있음
위와 같이 계산을 하는 부분을 새로운 클래스로 분리하여 구현하는 방법
네트워크 처리와 같이 외부에 연동하는 부분을 별도의 클래스로 분리
추상화에 관련된 포스트에서 언급했던 것 처럼, 여러개의 조건 분기가 걸려있을 경우 추상화를 통해 해결
왼쪽의 코드처럼 작성하게 되면, repository들과 연동하여 테스트를 진행해야함.
하지만, 오른쪽처럼 분리하게 된다면 PointCalculator만 따로 테스트를 할 수 있음.
public class CashClient {
private SecretKeySpec keySpec;
private IvParamterSpec ivSpec;
private Res post(Req req){
String reqBody = toJson(req);
Cipher cipher = Cipher.getInstance(DEFAULT_TRANSFORM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
String encReqBody = new String(Base64.getEncoder().encode(chipher.doFinal(reqBody)));
ResponseEntity<String> responseEntity = restTemplate.postForEntity(api, encReqBody, String.class);
String encRespBody = responseEntity.getBody();
Cipher cipher2 = Cipher.getInstance(DEFAULT_TRANSFORM);
cipher2.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
String respBody = new String(chipher2.doFinal(Base64.getDecoder().decode(encRespBody)));
return jsonToObj(respBody);
}
}
위 코드에서는 Cipher를 통한 암호화 및 복호화 ( 계산 기능 ) 을 분리할 수 있다.
분리하면 아래와 같다.
public class CashClient {
private Cryptor cryptor;
private Res post(Req req){
String reqBody = toJson(req);
String encReqBody = cryptor.encrypt(reqBody);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(api, encReqBody, String.class);
String encRespBody = responseEntity.getBody();
String respBody = cryptor.decrypt(encRespBody);
return jsonToObj(respBody);
}
}
public class Cryptor {
private SecretKeySpec keySpec;
private IvParamterSpec ivSpec;
public String encrypt(String plain){
Cipher cipher = Cipher.getInstance(DEFAULT_TRANSFORM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
return new String(Base64.getEncoder().encode(chipher.doFinal(reqBody)));
}
public String decrypt(String encrypted){
Cipher cipher = Cipher.getInstance(DEFAULT_TRANSFORM);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return new String(chipher.doFinal(Base64.getDecoder().decode(encRespBody)));
}
}
public class Rental {
private Movie movie;
private int daysRented;
public int getFrequentRenterPoints(){
if(movie.getPriceCode() == Movie.NEW_RELEASE && daysRented > 1 ){
return 2;
}
return 1;
}
}
public class Movie {
public static int REGULAR = 0;
public static int NEW_RELEASE = 1;
private int priceCode;
public int getPriceCode(){
return priceCode;
}
}
위 코드의 조건 분기를 추상화를 통해 분리하면 아래와 같다.
public class Rental {
private Movie movie;
private int daysRented;
public int getFrequentRenterPoints(){
return movie.getFrequentRenterPoints(daysRented);
}
}
public abstract class Movie {
public abstract int FrequentRenterPoints(int daysRented);
}
public class NewRelaseMovie extends Movie {
public int FrequentRenterPoints(int daysRented){
return daysRented > 1 ? 2 : 1;
}
}
public class RegularMovie extends Movie {
public int FrequentRenterPoints(int daysRented){
return 1;
}
}
위와 같이 Movie를 추상화하여 하위 클래스를 만들어 분리를 하였다.
전체 회원 가입기능을 웹 요청과 회원 가입으로 분리하고, 각각의 기능들을 더 작은 하위 기능들로 분리하였음.
실제로 기능을 분리하여 설계를 하면 위와 같다.