7. 코드를 오용하기 어렵게 만들라
방어적 디자인(defensive design) 원칙, EUHM(easy to use and hard to misuse)
7.1 불변 객체로 만드는 것을 고려하라
- 불변 (immutable): 객체 생성 후 상태를 바꿀 수 없음
- 가변 (mutability): 객체 생성 후 상태를 바꿀 수 있음
가변객체의 문제점은 아래와 같다.
- 추론하기 어렵다? => 신뢰하기 어렵다
- 다중 스레드에서 문제가 발생할 수 있다. (동시성 이슈)
객체를 불변으로 만드는 것이 항상 가능하지도 않고, 또 항상 적절한 것도 아니다. 필연적으로 상태 변화를 추적해야 하는 경우가 있고 이때는 가변적인 자료구조가 필요하다.
사실 객체 지향 프로그래밍에서는 가변객체를 선호한다.
7.1.1 가변 클래스는 오용하기 쉽다
무분별한 setter는 오용을 부추긴다.
7.1.2 해결책: 객체를 생성할 때만 값을 할당하라
setter대신 객체 생성 시 값을 초기화하는 전략을 사용하여 오용을 방지하라. (생성자 이용)
7.1.3 해결책: 불변성에 대한 디자인 패턴을 사용하라
초기화해야 하는 값이 너무 많거나, 객체의 속성 중에 필수와 선택값이 혼재해 있을 경우에는 ‘빌더 패턴’을 사용하는 것을 고려하라.
객체의 속성을 변경해야 한다면 불변성을 유지하기위해 ‘쓰기 시 복사 패턴’을 사용하는 것을 고려하라.
7.2 객체를 깊은 수준까지 불변적으로 만드는 것을 고려하라
객체는 불변이여도, 맴버 변수가 가변적이면서 그 맴버 변수에 대해 외부에서 접근이 가능한 경우에는 깊은 가변성(deep mutability)가 있다고 한다.
7.2.1 깊은 가변성은 오용을 초래할 수 있다
깊은 가변성이 있는 객체는 아래 2가지 이유로 오용 될 수 있다.
- 가변객체를 불변객체의 생성시 사용한 후, 가변객체를 조작하는 경우 (시나리오 A)
- 불변객체 내의 가변객체를 getter 등을 통해 접근한 후, 조작하는 경우 (시나리오 B)
7.2.2 해결책: 방어적으로 복사하라
getter등을 통해 가변객체에 대한 외부 접근이 가능할 경우, 가변객체의 복사본을 반환하는 것을 고려하라.
7.2.3 해결책: 불변적 자료구조를 사용하라
가변객체를 불변객체로 변경하거나, 랩핑하거나, 불변 인터페이스로 변환하는 것을 고려하라.
7.3 지나치게 일반적인 데이터 유형을 피하라
일반적이고 다재다능하다는 것을 뒤집어 생각해보면 데이터 유형 자체만으로는 무언가를 설명할 수 없고, 가질 수 있는 값에 있어서도 꽤 관대하다는 것을 의미한다.
7.3.1 지나치게 일반적인 유형은 오용될 수 있다
List나 Map, Set 같은 일반적인 자료구조는 아무것도 설명해주지 않는다. List의 경우 순번에 따라 데이터의 의미가 달라진다면, 혼동하기 쉬워지고 버그가 발생 할 가능성도 높아진다. 그리고 이러한 코드가 있으면 이 코드를 사용하는 쪽에도 같은 패러다임이 독버섯처럼 번져나가기 쉽다.
7.3.2 페어 유형은 오용하기 쉽다
일반적인 유형이면서 형식안정성을 높이기 위한 자료구조(ex. 페어 유형)도 같은 문제를 가지고 있다.
7.3.3 해결책: 전용 유형 사용
무언가를 나타내기 위해 새로운 클래스(또는 구조체)를 정의하는 것은 많은 노력이 들거나 불필요한 것처럼 보일 수 있지만, 대부분 보기보다 노력이 덜 들어가고 다른 개발자가 코드를 읽을 때 이해하기 쉽고 버그의 가능성도 줄여준다.
7.4 시간 처리
시간은 아래와 같은 부분을 고려해야 하기 때문에 까다롭다.
- 절대적 시간 (11시 36분), 상대적 시간 (지금으로 부터 5분 후)
- 시간의 양 (2시간)
- 시간대, 서머 타임, 윤년, 윤초
그렇기에 언어나 그 언어에서 일반적으로 사용하는 시간 라이브러리를 적절하게 사용하여 시간 처리를 하는 것이 좋다.
7.5 데이터에 대해 진실의 원천을 하나만 가져야 한다
- 기본 데이터(primary data): 코드에 제공해야 할 데이터. 코드에 이 데이터를 알려주지 않고는 코드가 처리할 방법이 없다.
- 파생 데이터(derived data): 주어진 기본 데이터에 기반해서 코드가 계산할 수 있는 데이터
기본 데이터는 일반적으로 프로그램에서 진실의 원천(source of truth)이 된다.
데이터 계산에 비용이 많이 드는 경우에 한해서 캐시로 저장하는 전략을 취하는 것을 고려하라.
7.6 논리에 대한 진실의 원천을 하나만 가져야 한다
진실의 원천(sources of truth)은 코드에 제공된 데이터에만 적용되는 것이 아니라 코드에 포함된 논리에도 적용된다.