[테스트 주도 개발] 16장_드디어, 추상화

Expression.plus 를 끝내려면, Sum.plus() 를 구현해야한다. 그리고, Expression.times() 를 구현하면 끝난다. 다음은 Sum.plus() 테스트다.

1
2
3
4
5
6
7
8
9
10
11
@Test
void testSumPlusMoney() {
Expression fiveBucks = Money.dollar(5);
Expression tenFrancs = Money.franc(10);
Bank bank = new Bank();
bank.addRate("CHF", "USD", 2);
Expression sum = new Sum(fiveBucks, tenFrancs).plus(fiveBucks);
Money result = bank.reduce(sum, "USD");
assertEquals(Money.dollar(15), result);

}

테스트가 코드보다 더 길다. 그리고 코드는 Money 의 코드와 똑같다.

1
2
3
4
5
//Sum
@Override
public Expression plus(Expression addend) {
return new Sum(this, addend);
}

일단, Sum.times() 가 작동하게 된다면, Expression.times() 를 선언하는 일은 쉽다. 테스트는,

1
2
3
4
5
6
7
8
9
10
@Test
void testSumTimes(){
Expression fiveBucks = Money.dollar(5);
Expression tenFrancs = Money.franc(10);
Bank bank = new Bank();
bank.addRate("CHF", "USD", 2);
Expression sum = new Sum(fiveBucks, tenFrancs).times(2);
Money result = bank.reduce(sum, "USD");
assertEquals(Money.dollar(20), result);
}
1
2
3
4
5
6
//Sum
Expression times(int multiplier) {
return new Sum(
augend.times(multiplier), addend.times(multiplier)
);
}

이제 컴파일되게 만들려면, Expression 에 times() 를 선언해야한다.

1
2
3
4
5
6
7
interface Expression {
Money reduce(Bank bank, String to);

Expression plus(Expression addend);

Expression times(int multiplier);
}

위 작업 때문에, Money.times() 와 Sum.times() 의 가시성을 높여야한다.

Read more

[빅데이터] 4장_일괄 처리 계층의 데이터 저장소

마스터 데이터 집합은하나의 서버에 저장하기에는 너무 크다. 데이터를 여러 장비에 어떻게 분산시킬 것인지기 중요하다.
이번 장은 다음을 정리한다.

  1. 마스터 데이터 집합을 저장하는데 필요한 요구사항
  2. 분산 파일 시스템이 왜 데이터 집합을 저장하는데 적합한지

4.1 마스터 데이터 집합 저장소의 요구사항

“데이터를 한 번만 쓰고, 읽기는 큰 단위로 여러 번 수행된다” 방식에 초점을 두면, 다음과 같이 요구사항을 정리할 수 있다.

  • 쓰기

    • 데이터 추가의 효율성 : 유일한 쓰기 연산은 새로운 데이터를 추가하는 것뿐이다.
    • 확장성 있는 저장소 : 데이터 집합이 커질 때 확장하기 쉬워야한다.
  • 읽기

    • 병렬 처리 지원 : 거대한 양의 데이터를 확장성 있는 방식으로 다룰 수 있도록 병렬 처리를 지원해야한다.
  • 둘 다

    • 저장 비용과 처리 비용 조율 : 필요에 따라 데이터를 저장학고 압축하는 방식을 선택하는 유연성이 있어야한다.
    • 불변성 강제

4.2 일괄 처리 계층을 위한 저장소 솔루션 선택

4.2.1 키/값 저장소

여러 장비에 분산되는 거대하고 영속적인 hash map

  1. 값은 저장할 데이터이다. 키는 무엇일까 ? 우리가 사용하는 데이터 모델에는 데이터 자체가 대량 소비를 전제로 한다. 그래서 원래 키가 없고, 필요하지도 않다. 즉, 처음 부터 데이터 모델과 키/값 저장소의 동작 방식이 맞지 않는 것이다. 유일하게 쓸만한 방법이라면, UUID 를 생성해서 키로 사용하는 것이다.
  2. 키/값 저장소는 무작위 읽기와 쓰기를 지원하기 위해 키/값 쌍에 세밀하게 접근해야한다. 그래서, 여러 개의 키/값 쌍을 묶어서 압축하는 것도 불가능하다.
  3. 키/값 저장소는 변경 가능한 저장소에 사용하도록 만들어져서, 마스터 데이터 집합에 불변성을 강제하는 게 극히 필요한 경우라면 문제가 된다.
  4. 키/값 저장소에는 불필요한 기능이 많다. 무작위 읽기, 쓰기 그리고 이들을 가능하게 하는 모든 장치들이 이에 해당된다.
  5. 데이터 색인을 하며 우리에게는 필요 없는 서비스도 제공해서 저장소 비용이 증가하고 데이터를 읽고 쓰는 성능이 저하될 수 있다.
4.2.2 분산 파일 시스템
Read more

[테스트 주도 개발] 7장_사과와 오렌지

제목 의미 : 서로 다른걸 비교할 수 없다.

Franc 와 Dollar 를 비교하면 어떻게 될까 ?

1
2
3
4
5
6
7
8
@Test
void testEquality() {
assertTrue(new Dollar(5).equals(new Dollar(5)));
assertFalse(new Dollar(5).equals(new Dollar(6)));
assertTrue(new Franc(5).equals(new Franc(5)));
assertFalse(new Franc(5).equals(new Franc(6)));
assertFalse(new Franc(5).equals(new Dollar(5)));
}

실패한다. 오직 금액과 클래스가 동일할 때만 두 Money 가 서로 같은 것이다.

1
2
3
4
5
public boolean equals(Object object) {
Money money = (Money) object;

return amount == money.amount && getClass().equals(money.getClass());
}

자바 객체의 용어를 사용하는 것보다 재정 분야에 맞는 용어를 사용하고 싶다. currency 통화 개념 같은 게 없고, 통화 개념을 도입할 충분한 이유가 없으므로 잠시 이렇게 두자.


테스트 주도 개발 <켄트 벡>

Read more

[테스트 주도 개발] 8장_객체 만들기

times() 의 구현 코드가 거의 같다.

1
2
3
4
5
6
7
8
9
// Franc Class
Franc times(int multiplier) {
return new Franc(amount * multiplier);
}

// Dollar Class
Dollar times(int multiplier) {
return new Dollar(amount * multiplier);
}

양쪽 모두 Money 를 반환하게 만들면 더 비슷해진다.

1
2
3
4
5
6
7
8
9
// Franc Class
Money times(int multiplier) {
return new Franc(amount * multiplier);
}

// Dollar Class
Money times(int multiplier) {
return new Dollar(amount * multiplier);
}

이제, Money 의 두 하위 클래스는 많은 일을 하지 않으므로 제거하고 싶다. 한번에 이렇게 큰 단계를 밟는 것은 TDD 를 효과적으로 보여주기 적절치 않을 것 같다.
하위 클래스에 대한 직접적인 참조가 적어지면 하위 클래스를 제거하기 쉬울 것 같다.

1
2
3
4
5
6
7
@Test
void testMultiplication() {
Dollar five = Money.dollar(5);
assertEquals(new Dollar(10), five.times(2));
assertEquals(new Dollar(15), five.times(3));
}

구현 코드는 Dollar 를 생성해 반환한다.

1
2
3
4
// Money Class
static Dollar dollar(int amount) {
return new Dollar(amount);
}

Test 선언부도 다음과 같이 변경한다.

1
2
3
4
5
6
7
@Test
void testMultiplication() {
Money five = Money.dollar(5);
assertEquals(new Dollar(10), five.times(2));
assertEquals(new Dollar(15), five.times(3));
}

Read more

[테스트 주도 개발] 9장_우리가 사는 시간

제목 : 영어에서 시간과 곱하기가 모드 ‘times’ 라는 점에서 착안한 말장난이다.
통화 개념을 어떻게 테스트하길 원하는가 ? 통화를 표현하기 위한 복잡한 객체를 만들 수도 있다. 그리고, 그 객체들이 필요한 만큼만 만들어지도록 하기 위해 flyweight factories 경량 팩토리를 사용할 수 있을 것이다. 하지만, 당분간 대신 문자열을 쓰자.

1
2
3
4
5
6
@Test
void testCurrency(){
assertEquals("USD", Money.dollar(1).currency);
assertEquals("CHF", Money.franc(1).currency);
}

우선, Money 에 currency() 메서드를 선언하자.

1
abstract String currency();

그리고 이를, 두 하위 클래스에서 구현하자.

1
2
3
4
5
6
7
8
9
10
11
// Dollar Class
@Override
String currency() {
return "USD";
}

// Franc Class
@Override
String currency() {
return "CHF";
}

우린 두 클래스를 모두. 포함할 수 있는 동일한 구현을 원한다. 통화를 인스턴스 변수에 저장하고, 메서드에서는 그냥 그걸 반환하게 할 수 있을 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Franc extends Money {
private String currency;

Franc(int amount) {
this.amount = amount;
currency = "CHF";
}

Money times(int multiplier) {
return new Franc(amount * multiplier);
}

@Override
String currency() {
return currency;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Dollar extends Money {
private String currency;

Dollar(int amount) {
this.amount = amount;
currency = "USD";
}

Money times(int multiplier) {
return new Dollar(amount * multiplier);
}

@Override
String currency() {
return currency;
}
}

이제 두 currency() 가 동일하므로 변수 선언과 currency() 구현을 모두 위로 올릴 수 있다.

Read more