3장 모두를 위한 평등.
Dollor 객체를 값 객체 패턴으로 설명한다.
값 객체
- immutable : (생성자에 의해 생성된 후)변경 불가능한 객체이다.
- 값 객체는 [두개의 값 객체의 동일성(equality) 은 Identity에 기반하지 않고 그들의 컨텐츠로 한다]로 정의된다.
==> Identity : 데이터베이스의 인덱스 처럼 자신을 나타내는 요소, 컨텐츠가 같을 때 두 객체를 구분하는 요소
- 별칭문제를 해결할 수 있다. (dollor가 $5일때 영원히 5의 값을 가짐을 보장, $7을 만들고 싶다면 새로운 객체를 만들어야함.)
이를 통해 값 객체가 암시하는 것을 알 수 있다.
1. 모든 연산은 새 객체를 반환해야 한다.
2. equals()를 통해 같음을 보장받아야 한다.
값 객체는 다음 세단계로 만들 수 있다.
1. 인스턴스 변수를 private와 같이 외부에서 접근할 수 없게 막는다.
2. 생성자를 통해 인스턴스 변수의 값을 초기화시킨다.
3. setter와 같은 값을 외부의 메소드로 수정할 수 있는 메소드를 만들지 않는다.
외부에서 속성을 바꿀 수 없음이 보장되면, 객체의 동일성은 주소가 같음이 아닌 값의 같음으로 정의될 수 있다.
<할일 목록>
$5 + 10CHF = $10(환율이 2:1 인 경우)
// 해결 // $5 x 2 = $10
amount를 private로 만들기
// 해결 // Dollor 부작용?
Money 반올림
// 추가 // equals()
// 추가 // hashCode()
dollor를 해시테이블의 키로 쓸 생각이라면 hashCode()도 구현해야 한다.(어떤의미인지 잘 모르겠음. 뒤에 나온다고 함.)
equals메소드를 만들고,
가짜 구현을 통해 테스트를 통과시켰다.
여기서 '삼각측량'법이 나온다.
삼각측량 - 두개의 예제를 통해 코드를 일반화 시킨다.
삼각측량법은 어떻게 리펙토링해야 하는지 전혀 감이 안올때 사용한다.(조금 다른 방향에서 볼 기회를 제공)
예제 테스트를 하나 더 추가시켰고 두 테스트를 통과 시키기 위해 equals메소드를 일반화 시켰다.
동일성 문제는 일시적으로 해결했지만 널 값과 다른 객체와 비교하는 경우도 있을 수 있다. 할일에 추가.
<할일 목록>
$5 + 10CHF = $10(환율이 2:1 인 경우)
// 해결 // $5 x 2 = $10
amount를 private로 만들기
// 해결 // Dollor 부작용?
Money 반올림
// 해결 // equals()
hashCode()
//추가//Equal null
//추가//Equal object
4장. 프라이버시
클래스의 필드는 내부적으로 이용하고, 외부에게서 보호받기 위해 private 변수로 지정할 필요가 있다.
먼저번에서 작성한 테스트에서 10과 product.amount를 비교하는 것을 객체끼리 비교하는 것으로 바꿔보자.
위의 사진처럼 테스트를 바꿔주면, 외부에서 amount를 쓰지 않으므로 private형 필드로 바꿔줄 수 있다.
여기서 우리는 하나의 위험을 가지고 갔는데,
동치성에 대한 테스트(equals())가 정확하다는 것에 검증을 실패한다면, 그 안에서 비교중인 times()메소드도 정확하다는 검증을 내릴 수 없다.
이 위험성에 대해 항상 주의하고, 실패를 경험한다면 코드를 어떻게 짰는지 살펴보고 교훈을 얻고 다시 나아가야 한다.
5장. 솔직히 말하자면
우리의 할 일 중 첫번째, 환율을 고려한 테스트는 너무 크고 복잡해 보인다. 이를 위해 작은 단계부터 해결하자.
<할일 목록>
$5 + 10CHF = $10(환율이 2:1 인 경우)
// 해결 // $5 x 2 = $10
// 해결 // amount를 private로 만들기
// 해결 // Dollor 부작용?
Money 반올림
// 해결 // equals()
hashCode()
Equal null
Equal object
//추가// 5CHF x 2 = 10CHF
방금 추가된 할 일을 해결하기 위해 dollor에서 쓴 테스트를 이름만 바꿔서 추가한다.
이 컴파일되지 않는 테스트를 빨리 초록막대를 보는 방법은 dollor객체와 비슷한 france객체를 만드는 것이다.
위의 두 과정에서 중복이 발생한다. 중복을 나중에 해결하기로 하고 일단 넘어가자.
검토해보면 ,
큰 테스트를 위한 작은 테스트를 만들었다
> 중복을 만들고 조금 고쳐서 테스트를 작성했다.
> 모델 코드를 복사해서 사용했다. 여기서도 중복이 발생했다. (equal, times)
> 중복을 해결하기로 마음먹어야 한다.
<할일 목록>
$5 + 10CHF = $10(환율이 2:1 인 경우)
// 해결 // $5 x 2 = $10
// 해결 // amount를 private로 만들기
// 해결 // Dollor 부작용?
Money 반올림
// 해결 // equals()
hashCode()
Equal null
Equal object
//해결// 5CHF x 2 = 10CHF
//추가// Dollor,Franc 중복
//추가// 공용 equal
//추가// 공용 times
6장. 돌아온 모두를 위한 평등
3장에서 모두를 위한 평등을 위해 equals()메소드를 정의했다. 이번장의 제목에서 알 수 있듯이, 모두를 위한 평등이란
5장에서 새로 만든 Franc객체에 대해서도 평등을 만족하는 테스트, 하지만 중복을 고친 테스트를 진행한다.
현재 5장에서 Franc와 Dollor객체에 대해서 해결해야 하는 중복 일을 세개 추가했다.
이 중 모두를 위한 평등을 위해 equals()메소드의 중복을 상위 클래스의 상속을 통해 해결한다.
1. 상위 클래스 Money 생성
2. 하위 클래스에서 접근가능한 protected변수 amount 선언
3. Dollor클래스가 Money클래스를 상속받음
4. 상속으로 인해 상위 클래스의 amount사용(삭제가능)
5. equals()안의 Dollor객체를 Money객체로 이용
6. 원활한 소통을 위해 변수명 변경
7. 이제 Money클래스로 equals()메소드를 이동 가능
이제는 Franc클래스의 equals()를 제거해야 하는데, 우리가 작성했던 testEquality테스트에서는 Franc에 대한 동치성을 검사하지 않았다.
코드를 복사하면서 생긴 죄악( 복사하지 않았으면 작성했을 것이던 테스트 )이 발목을 붙잡았다.
이때, 테스트를 추가하지 않아도 테스트는 돌아갈 것이다.(아예 안하는 것이니까..)
하지만 이럴 때, 있으면 좋을 것 같은 테스트를 추가한다. 하지 않으면 결국 리펙토링 과정에서 뭔가가 깨질 것이다.
이제 Dollor에서 중복을 제거한 것처럼 Franc에서도 중복을 제거한다.
테스트가 통과한다.
우린 결국 Dollor, Franc에서의 equals()중복을 해결했다.
<할일 목록>
$5 + 10CHF = $10(환율이 2:1 인 경우)
// 해결 // $5 x 2 = $10
// 해결 // amount를 private로 만들기
// 해결 // Dollor 부작용?
Money 반올림
// 해결 // equals()
hashCode()
Equal null
Equal object
//해결// 5CHF x 2 = 10CHF
Dollor,Franc 중복
//해결// 공용 equal
공용 times
//추가// Franc와 Dollor비교하기
하지만 테스트에서는 달러는 달러끼리 비교하고 있다.
Dollor와 Franc끼리의 비교를 할 일에 추가한다.
7장에서 한 일을 되짚어보면,
1. 공통된 코드를 Dollor에서 상위 클래스 Money로 단계적으로 옮김.
2. Franc라는 두번째 클래스도 하위 클래스로 만듦.
3. 불필요한 구현을 제거하기 전에, 두 equals()의 구현을 일치시켰다.