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

Lee Han Sol·2021년 9월 15일
0

객체 지향 프로그래밍 입문

이 글은 최범균님의 Inflearn 강의를 학습한 내용을 정리하였습니다.

캡슐화 연습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.isPasswordVaild(mem.getPassword(), pw, mem.getId())) {
      return AuthResult.SUCCESS;
  }
  return AuthResult.NO_MATCH;
}

코드 설명

위 코드는 인증과 관련된 코드로 아이디와 암호를 매개변수로 받아

  1. 아이디에 해당하는 맴버가 없으면 NO_MATCH를 리턴
  2. 맴버에 verificationEmailStatus() 값이 2가 아니라면 NO_EMAIL_VERIFIED를 리턴
  3. 암호가 유효하다면 SUCCESS를 리턴

한다.

캡슐화 적용

예제 코드를 캡슐화하기 위해 적용할 수 있는 규칙은 Tell, Don't Ask (데이터를 달라하지 말고 해달라고 하기)다.

아래의 조건문을 보면

if (mem.getVerificationEmailStatus() != 2) { ... }

맴버 객체에게 verificationEmailStatus 값을 받아서 2가 아닌지 판단하고 있다.

값을 받지말고 연산한 결과를 받아오도록 수정하면 아래와 같다.

이와같이 캡슐화를 수행하면 나중에 verificationEmailStatus가 3일 경우 인증된 상태라고 바뀌어도 내부 구현만 수정하고 외부의 코드는 수정하지 않아도 된다.

캡슐화 연습2

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

출처: 리팩토링(마틴 파울러 저)

코드 설명

  1. Movie.java
    영화 가격 코드를 담당하는 객체이다.
    최신 영화라면 NEW_RELEASE를 일반 영화라면 REGULAR를 priceCode로 설정한다.

2.Rental.java
영화 대여를 담당하는 객체이다.
최신 영화를 하루 이상 대여했다면 2포인트를 주고 그렇지 않다면 1포인트를 리턴한다.

캡슐화 적용

이 예제 역시 데이터를 달라고 하는 부분을 캡슐화할 수 있다.

아래의 코드를 Movie 객체에게 부탁하자.

movie.getPriceCode() == Movie.NEW_RELEASE 

결과는 아래와 같다.

하지만 이 코드 역시 뭔가 아쉽다.

조건 전체를 Movie 객체에 맡겨보자.

movie.isNewRelease() && daysRented > 1

최종 결과는 아래와 같다.

이렇게 캡슐화를 하면

  1. NEW_RELEASE 또는 REGULAR에 따라서 포인트를 구하는 공식이 바뀐 경우
  2. daysRented가 2보다 커야 될 경우

Movie 객체의 getFrequentRenterPoints의 코드만 변경하면 된다.

캡슐화 예제2는 데이터를 들고 있는 객체가 기능을 제공하면서 해당 기능에 필요한 다른 값을 파라미터로 받는 경우이다.

캡슐화 연습3

//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)

코드 설명

  1. Timer.java
    startTime과 stopTime의 데이터를 가지고 있다.

  2. 실행 시간 측정 코드
    타이머 데이터 클래스에 시작 시간과 종료 시간을 저장하고 로직의 실행 시간을 구하는 코드이다.

캡슐화 적용

위 코드는 Timer 데이터 클래스를 가져다 사용하는 절차 지향적으로 작성되어있다.
캡슐화를 적용하기 전에 주석이 있는 코드(1), (2), (3)의 기능을 알아보자.

  • (1) t.startTime = System.currentTimeMillis()
    시작 시간을 구한다.
  • (2) t.stopTime = SyStem.currentTimeMillis()
    종료 시간을 구한다.
  • (3) t.stopTime - t.startTime
    소요 시간을 구한다.

각 기능을 Timer 객체로 묶어주면 코드는 아래와 같다.

기존 코드에서 데이터 클래스로 사용되던 Timer가 기능을 제공하는 객체로 바뀌었다.
Timer를 캡슐화함으로써 측정 시간 단위를 millisecond에서 nanosecond로 변경하더라도 외부의 코드는 변경하지 않고 내부의 구현을 변경하면된다.

캡슐화 연습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 반영
}

코드 설명

  1. token을 매개변수로 받아 회원을 조회한다.
  2. 만약 회원이 없다면 예외를 발생시킨다.
  3. 그리고 verificationEmailStatus가 2라면 이미 확인되었다는 예외를 발생시킨다.
  4. 그렇지 않다면 verificationEmailStatus 값을 2로 설정한다.
  5. 이후 수정사항을 DB에 반영한다.

캡슐화 적용

연습1, 2, 3 과정과 비슷하게 객체에 맡기고 싶은 부분은

  1. mem.getVerificationEmailStatus() == 2
  2. mem.setVerificationEmailStatus(2)

    조건을 검사하는 코드(1)와 데이터를 변경하는 코드(2) 이렇게 두 부분이다.

    1번을 캡슐화한 코드는 아래와 같다.

    조건 검사를 캡슐화했지만 코드의 구조는 개선되지 않았다.

    이 코드의 구조는 데이터를 가져와서 판단하고 결과에 따라서 데이터를 바꾼다.

이러한 코드는 조건문을 통으로 캡슐화했을 때 개선될 가능성이 높아진다.
개선한 코드는 아래와 같다.

profile
주 7일, 배움엔 끝이 없다

0개의 댓글