12장 - 창발성

창발적 설계로 깔끔한 코드를 구현하자

컨트 벡이 제시한 단순한 설계 규칙 네 가지 (중요도 순)

  1. 모든 테스트를 실행한다.
  2. 중복을 없앤다.
  3. 프로그래머 의도를 표현한다.
  4. 클래스와 메서드 수를 최소로 줄인다.

단순한 설계 규칙 1 : 모든 테스트를 실행하라

테스트를 철저히 거쳐 모든 테스트 케이스를 항상 통과하는 시스템은 테스트가 가능한 시스템이다.

테스트가 가능한 시스템을 만들려고 애쓰면 설계 품질이 더불어 높아진다.
크기가 작고 하나만 수행하는 클래스가 나온다.

SRP Single Responsibility Principle, 단일 책임 원칙 (모든 클래스는 하나의 책임만 가져야 하는 원칙)를 준수하는 클래스는 테스트가 훨씬 더 쉽다.

테스트 케이스를 많이 작성할수록 개발자는 DIP Dependency Inversion Principle, 의존성 역전 원칙 (변화하지 않는 것에 의존하라는 원칙)와 같은 원칙을 적용하고 의존성 주입 Dependency Injection, 인터페이스, 추상화 등과 같은 도구를 사용해서 결합도를 낮춘다.

단순한 설계 규칙 2~4 : 리팩터링

테스트 케이스를 모두 작성했다면 이제 클래스를 정리해도 괜찮다. 구체적으로는 코드를 점진적으로 리팩터링 해나간다.

코드를 정리하면서 시스템이 깨질까 걱정할 필요가 없다. 테스트 케이스가 있으니까!

중복을 없애라

우수한 설계에서 중복은 커다란 적이다. 중복은 추가 작업, 추가 위험, 불필요한 복잡도를 뜻하기 때문이다.

소규모 재사용

public void  scaleToOneDimension(float desireDimension, float imageDimension) {
    if(Math.ads(desiredDimension - imageDimension) < errorThreshold) return;

    float scalingFactor = desiredDimension / imageDimension;

    scalingFactor = (float) (Math.floor(scalingFactor * 100) * 0.01f);

    replaceImage(ImageUtilities.getScaledImage(image, scalingFactor, scalingFactor));
}

public sychronized void rotate(int degrees) {
    replaceImage(ImageUtilities.getRotatedImage(image, degress));
}

private void replaceImage(RenderedOp newImage) {
    image.dispose();
    System.gc();
    image = newImage;
}

깔끔한 시스템을 만들려면 단 몇 줄이라도 중복을 제거하겠다는 의지가 필요하다.
이런 소규모 재사용은 시스템 복잡도를 극적으로 줄여준다.

TEMPLATE METHOD1

abstract public class VacationPolicy {
    public void accureVacation() {
        caluateBaseVacationHours();
        alterForLegalMinimums();
        applyToPayroll();
    }

    priavte void caluateBaseVacationHours() { /* ... */}
    abstract alterForLegalMinimums();
    private void applyToPayroll () { /* ... */}
}

public class USvacationPolicy extends VactionPolicy {
    @Override protected void alterForLegalMinimums() {
        //미국 최소 법정 일수를 사용한다.
    }
}

public class EUvacationPolicy extends VactionPolicy {
    @Override protected void alterForLegalMinimums() {
        //유럽연합 최소 법정 일수를 사용한다.
    }
}

TEMPLATE METHOD 패턴은 디자인 패턴 중 하나로, 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내용을 바꾸는 패턴이다.
즉, 전체적으로는 동일하면서 부분적으로는 다른 구문으로 구성된 메서드의 코드 중복을 최소화할 때 유용하다.

표현하라

개발자가 코드를 명백하게 짤수록 다른 사람이 그 코드를 이해하기 쉬워진다. 그래야 결함이 줄어들고 유지보수 비용이 적게 든다.

  1. 좋은 이름을 선택한다.
  2. 함수와 클래스 크기를 가능한 한 줄인다.
  3. 표준 명칭을 사용한다. (ex. 클래스가 COMMAND나 VISITOR와 같은 표준 패턴을 사용해 구현된다면 클래스 이름에 패턴 이름을 넣어준다.)
  4. 단위 테스트 케이스를 꼼꼼히 작성한다.

나중에 코드를 읽을 사람은 바로 자신일 가능성이 높다는 사실을 명심하자.

클래스와 메서드 수를 최소로 줄여라

때로는 무의미하고 독단적인 정책 탓에 클래스 수와 메서드 수가 늘어나기도 한다.
목표는 함수와 클래스 크기를 작게 유지하면서 동시에 시스템 크키도 작게 유지하는 데 있다.
하지만 네 개의 규칙 중 우선순위가 가장 낮다.
따라서 테스트 케이스를 만들고 중복을 제거하고 의도를 표현하는 작업이 더 중요하다.

결론

단순한 설계 규칙을 따른다면 우수한 기법과 원칙을 단번에 활용할 수 있다.

Footnotes

  1. https://gmlwjd9405.github.io/2018/07/13/template-method-pattern.html