3. 클래스 설계 - 모든 것과 연결되는 설계 기반

내용


클래스 단위로 잘 동작하도록 설계하기

  • 클래스 하나라도 잘 동작하도록 설계해야한다.

    • 클래스를 마음대로 조작해서 클래스 전체가 고장 나는 일 없게, 최소한의 조작 방법만 외부에 공개해야함

  • 클래스 구성요소

    • 인스턴스 변수

    • 메서드

  • 잘 만들어진 클래스의 구성요소

    • 인스턴스 변수

    • 인스턴스 변수에 잘못된 값이 할당되지 않게 막고, 정상적으로 조작하는 메서드

  • 미성숙한 클래스

    • 다른 클래스를 사용해서 초기화와 유효성 검사를 해야하는 클래스

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

  • 기본

class Money {
	int amount;        // 금액
	Currency currency; // 통화 단위
}
  • 생성자로 확실하게 정상적인 값을 설정하기

    • 인스턴스 변수가 모두 들어가 있을 때만 초기화 하도록 보장하기

  • 생성자로 들어오는 값을 유효성 검사하기

    • 정상적인 값만 들어오는 지 확인하기

class Money {
	int amount;        // 금액
	Currency currency; // 통화 단위

	// 생성자로 인스턴스 변수가 무조건 초기화 되도록
	Money(int amount, Currency currency) {
		if (amount < 0) {
			// throw error
		}

		if (currency == null) {
			// throw NPE
		}
		this.amount = amount;
		this.currency = currency;
	}

}
  • 계산 로직도 데이터를 가진쪽에 구현해야함

    • 데이터 - 데이터를 조작하는 로직이 분리되어있다면 -> '응집도가 낮은 구조'

class Money {
	int amount;        // 금액
	Currency currency; // 통화 단위
	
	// 생략..

	// 데이터를 조작하는 로직
	void add(int other) {
		this.amount += other
	}
}
  • 불변 변수로 만들어서, 예상하지 못한 동작 막기

    • 인스턴스 변수를 변경하는 코드는 이해하기 어려움

    • 변수의 값이 계속 바뀌면, 추적하기 어려움

      • 예상하지 못한 부수 효과 발생

class Money {
	final int amount;        // 금액
	final Currency currency; // 통화 단위

	// 생략..
}
  • 변경하고 싶다면? 새로운 인스턴스를 만들기

    • 인스턴스 변수를 항상 불변을 유지하면서, 값 변경에도 대응하기

class Money {
	final int amount;        // 금액
	final Currency currency; // 통화 단위

	// 생략..

	Money add(int other) {
		int added = this.amount + other
		return new Money(added, this.currency);
	}
}
  • 메서드 매개변수와 지역변수도 불변으로 만들기

    • 왜?

      • 메서드의 매개, 지역변수도 메서드 내부에서 변경할 수 있으므로,

class Money {
	final int amount;        // 금액
	final Currency currency; // 통화 단위

	// 생략..

	Money add(final int other) {
		final int added = this.amount + other
		return new Money(added, this.currency);
	}
}
  • 엉뚱한 값(잘못된 값)을 전달하지 않도록 하기

    • other에 Money가 아닌 다른 값이 들어가는 것을 막기

class Money {
	final int amount;        // 금액
	final Currency currency; // 통화 단위

	// 생략..

	Money add(final Money other) {
		final int added = this.amount + other.amount
		return new Money(added, this.currency);
	}
}
  • 의미 없는 메서드 추가하지 않기

    • 지금 필요한 메서드만 구현한다.

    • 시스템 사양에 필요하지 않은 메서드를 선의로 추가하지 말자

    • 버그 유발할 수 있음.

프로그램 구조의 문제 해결에 도움을 주는 디자인 패턴

  • 디자인 패턴 == 설계 패턴

    • 완전 생성자(Complete Constructor)

      • 잘못된 상태로 부터 보호

    • 값 객체 (Value Object)

      • 특정한 값과 관련된 로직 응집도를 높이고

    • 전략(Strategy)

      • 조건 분기를 줄이고, 로직을 단순화하고,

    • 정책(Policy)

      • 조건 분기를 단순화하고, 더 자유롭게

    • 일급 컬렉션 (First Class Collection)

      • 값 객체의 일종

      • 컬렉션 관련된 로직의 응집도를 높이고

    • 스프라우트 클래스 (Sprout Class)

      • 기존 로직을 변경하지 않고,

      • 안전하게 새로운 기능을 추가

  • 완전 생성자

    • 인스턴스 변수를 모두 초기화해야만 객체를 생성할 수 있도록 함

    • '쓰레기 객체' 생성을 방지하고,

    • 생성자에서 가드로직을 통해 잘못된 값이 전달되는 것을 방지함

    • 완전한 객체 생성을 보장

  • 값 객체

    • e.g. 금액, 날짜, 주문 수, 전화번호 등

정리


  • 클래스는 그 자체로 온전해야한다

  • 응집력있는 클래스를 설계하라

  • 불변 클래스를 지향하라

  • 효과

    • 중복 코드 없애고

    • 수정 누락이 발생하지 않고

    • 가독성이 저하되지 않고,

    • 쓰레기 객체가 생성되지 않고

    • 생성하면서 잘못된 값이 들어오지 않고

    • 생각지도 않은 부수효과가 발생하지도 않고..

    • 값 전달 실수가 발생하지 않고

참고


  • https://product.kyobobook.co.kr/detail/S000202521361

Last updated

Was this helpful?