성숙한 클래스로 성장시키는 설계 기법

2023. 12. 3. 20:11객체지향

class Money {
  #amount;
  #currency;
}

다음과 같은 전형적인 데이터 클래스가 존재한다고 생각하자.

생성자로 확실하게 정상적인 값 설정하기

다른 클래스에서 해당 데이터의 초기화를 하지 않도록 하기 위해서는 constructor에 파라미터로 값을 전달하면 된다.

class Money {
  #amount;
  #currency;

  constructor(amount, currency) {
    this.#amount = amount;
    this.#currency = currency;
  }
}

가드 활용하기

하지만 잘못된 값이 전달될 수 있다.
이럴 때는 가드를 활용해서 잘못된 요소를 메서드 앞부분에 제외할 수 있으므로 이어지는 로직이 간단해진다.

가드를 활용하면 잘못된 값을 가진 인스턴스가 존재할 수 없게 된다.

class Money {
  #amount;
  #currency;

  constructor(amount, currency) {
    this.#validateAmount(amount);
    this.#validateCurrency(currency);
    this.#amount = amount;
    this.#currency = currency;
  }

  #validateAmount(amount) {
    if (amount < 0) {
      throw new Error('금액은 0 이상의 값을 지정해주세요.');
    }
  }

  #validateCurrency(currency) {
    if (currency === null) {
      throw new Error('통화 단위를 올바르게 지정해주세요.');
    }
  }
}

응집도를 높이기

데이터와 데이터를 조작하는 로직이 분리되어 있으면 응집도가 낮은 구조라고 한다.
응집도가 낮다는 것은 데이터를 처리하는 로직이 여러 클래스에서 구현될 가능성이 높다는 것이다. 이것은 후에 리팩터링에 어려움을 겪을 수 있고, 코드 중복이 생길 수 있다.

따라서 계산로직도 클래스 내부에서 구현하여 응집도를 높인다.

class Money {
  #amount;
  #currency;

  constructor(amount, currency) {
    this.#validateAmount(amount);
    this.#validateCurrency(currency);
    this.#amount = amount;
    this.#currency = currency;
  }

  add(other) {
    this.#amount += other;
  }

  #validateAmount(amount) {
    if (amount < 0) {
      throw new Error('금액은 0 이상의 값을 지정해주세요.');
    }
  }

  #validateCurrency(currency) {
    if (currency === null) {
      throw new Error('통화 단위를 올바르게 지정해주세요.');
    }
  }
}

인스턴스 변수를 불변 변수로 만들기

인스턴스 변수가 계속 바뀌게 되면 값이 언제 변경되었는지, 지금 값은 무엇인지에 대해 계속 신경써야한다.
또한 의도하지 않은 값을 할당하는 예상치못한 부수효과가 발생할 수 있다.

예를 들어 위의 코드에서 amount가 500인데 add메서드의 파라미터로 -600을 넣는다고 가정해보자.
금액은 0 이상의 값이어야 하지만 음수가 된다. 가드에서 유효성검사를 거치지도 않는다.

이럴때는 새로운 인스턴스를 반환하도록 수정하자.

  add(other) {
    const added = this.#amount + other;

    return new Money(added, this.#currency);
  }

엉뚱한 값을 전달하지 않도록 하기

add메서드에는 Moneyamount가 들어와야한다.
하지만 티켓 갯수와 같은 엉뚱한 값이 들어올 수도 있다.
엉뚱한 값이 전달되지 않도록 하려면, Money 타입만 매개변수로 받을 수 있도록 메서드를 변경해야한다.

자바스크립트에서는 타입이 없기 때문에 instanceOf를 이용해 구현했다.

class Money {
  #amount;
  #currency;

  constructor(amount, currency) {
    this.#validateAmount(amount);
    this.#validateCurrency(currency);
    this.#amount = amount;
    this.#currency = currency;
  }

  getAmount() {
    return this.#amount;
  }

  add(other) {
    if (other instanceof Money) {
      const added = this.#amount + other.getAmount();

      return new Money(added, this.#currency);
    }

    throw new Error('금액만 더해주십시오.');
  }

  #validateAmount(amount) {
    if (amount < 0) {
      throw new Error('금액은 0 이상의 값을 지정해주세요.');
    }
  }

  #validateCurrency(currency) {
    if (currency === null) {
      throw new Error('통화 단위를 올바르게 지정해주세요.');
    }
  }
}

'객체지향' 카테고리의 다른 글

초기화 로직 분리  (0) 2023.12.04