[객체 지향 프로그래밍 입문] 캡슐화 연습

kshired·2021년 8월 18일
0

이전 포스트에서 캡슐화를 알아보았으니, 실제 코드를 통해 캡슐화를 연습해보겠습니다.

캡슐화 연습 1

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.isPasswordValid(mem.getPassword(), pw, mem.getId())){
		return AuthResult.SUCCESS;
	}

	return AuthResult.NO_MATCH;
}

위 코드의 어떤 부분을 어떻게 캡슐화 할 수 있을까요?

Tell, Don't Ask 를 생각하면 mem.getVerificationEmailStatus() != 2 와 같이 직접적으로 데이터를 가져와서 비교하는 코드보다 객체의 기능으로 캡슐화하는 것이 좋습니다.

그래서 변경을 하면 아래와 같이 변경할 수 있습니다.

public AuthResult authenticate(String id, String pw){
	Member mem = findOne(id);
	if(mem == null) return AuthResult.NO_MATCH;

	if(!mem.isEmailVerified()){ // 변경된 부분
		return AuthResult.NO_EMAIL_VERIFIED;
	}

	if(passwordEncoder.isPasswordValid(mem.getPassword(), pw, mem.getId())){
		return AuthResult.SUCCESS;
	}

	return AuthResult.NO_MATCH;
}

public class Member {
	private int verificationEmailStatus;

	public boolean isEmailVerified(){
		return verificationEmailStatus == 2;
	}
}

위와 같이 변경하게 된다면, isEmailVerified() 의 내부 구현이 변경되어도 문제가 생기지 않는다.

캡슐화 연습 2

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;
	}
}

public class Movie {
	public static int REGULAR = 0;
	public static int NEW_RELEASE = 1;
	private int priceCode;
	
	public int getPriceCode(){
		return priceCode;
	}
}

위 코드를 어떻게 캡슐화할까요?

연습1에서 처럼, movie.getPriceCode() == Movie.NEW_RELEASEmovie.isNewRelease() 와 같이 캡슐화를 할 수도 있지만 딱히 크게 변하지 않는 것 같습니다.

이번에는, if-else 구문을 전부 캡슐화해보겠습니다.

public class Rental {
	private Movie movie;
	private int daysRented;

	public int getFrequentRenterPoints(){
		return movie.getFrequentRenterPoints(daysRented);
	}
}

public class Movie {
	public static int REGULAR = 0;
	public static int NEW_RELEASE = 1;
	private int priceCode;
	
	public int getFrequentRenterPoints(int daysRented){
		if(priceCode == NEW_RELEASE &&
			 daysRented > 1)
			return 2;
		else
			return 1;
	}
}

위와 같이 캡슐화하게 되면, 대여 포인트를 얻게 되는 코드의 로직이 변경되어도 getFrequentRenterPoints 를 사용하는 다른 클래스의 코드는 수정할 필요가 없어지고 Movie에서만 수정하면 됩니다.

캡슐화 연습 3

Timer t = new Timer();
t.startTime = System.currentTimeMillis();

//...

t.stopTime = System.currentTimeMillis();

long elapsedTime = t.stopTime - t.startTime;

public class Timer {
	public long startTime;
	public long stopTime;
}

위 코드는 모두 데이터를 가져와서 직접 계산하는 절차지향적인 코드입니다.

이것을 캡슐화해보겠습니다.

Timer t = new Timer();
t.start()

//...

t.stop();

long time = t.elapsedTime(MILLISECOND);

public class Timer {
	private long startTime;
	private long stopTime;

	public void start(){
		startTime = System.currentTimeMillis();
	}

	public void stop(){
		stopTime = System.currentTimeMillis();
	}
	
	public long elapsedTime(TimeUnit unit){
		switch(unit){
			case MILLISECOND:
				return stopTime - startTime;
			//...
		}
	}
}

위와 같이 캡슐화를 하게 되면 직접 데이터를 가져와 사용하는 것이 사라졌고, 메소드를 호출함으로써 얻으려는 데이터를 얻게 되었습니다.

그리고 위 캡슐화에서 elapsedTime 의 내부 구현으로 Nano초 단위로 데이터를 얻는 기능을 추가한다해도 long time = t.elapsedTime(MILLISECOND); 은 변경 될 필요가 없다는 것을 통해 캡슐화의 장점을 다시 한 번 알게 될 수 있습니다.

캡슐화 연습 4

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 반영
}

위와 같이 데이터를 가져오고, 판단을 한 뒤 데이터를 변경하는 코드는 통째로 캡슐화를 하면 캡슐화의 이점을 얻을 수 있습니다.

public void verifyEmail(String token){
	Member mem = findByToken(token);
	if(mem == null) throw new BadTokenException();
	
	mem.verifyEmail();
	// 수정 사항 DB 반영
}

public class Member {
	private int verificationEmailStatus;

	public void verifyEmail(){
		if(isEmailVerified()){
			throw new AlreadyVerifiedException();
		} else {
			verificationEmailStatus = 2;
		}
	}

	public boolean isEmailVerified(){
		return verificationEmailStatus == 2;
	}
}

위와 같이 캡슐화를 하게 되면, verifyEmail이라는 기능은 Member에서 내부 구현을 변경하더라도 여러 다른 클래스에서 영향을 받지 않습니다.

profile
글 쓰는 개발자

0개의 댓글