
이 장에서는 창발적 설계로 깔끔한 코드를 구현하는 것에 대해 알아본다.
창발성이란?
- 작은 요소들이 모였을 때, 개별 요소에서는 볼 수 없는 새로운 특성이나 행동이 나타나는 현상
- 복잡한 시스템에서 자발적으로 새로운 질서나 패턴이 생기는 것
- 프로그램 개발에서는 여러 단순한 모듈, 함수, 알고리즘들이 상호작용하면서, 설계자가 의도하지 않았거나 예상하지 못한 복잡한 동작이나 시스템 특성이 자연스럽게 나타나는 것
착실하게 따르기만 하면 우수한 설계가 나오는 간단한 규칙 네 가지가 있다. 다음은 해당 규칙을 중요도 순으로 나열하였다.
테스트 케이스를 만들고 계속 테스트하라는 규칙을 따르면 시스템은 낮은 결합도와 높은 응집력이라는, 객체지향방법론이 지향하는 목표를 저절로 달성한다. 테스트 케이스를 작성하면 설계 품질이 높아진다.
테스트 케이스 작성 후 코드와 클래스를 정리한다. 구체적으로, 코드를 점진적으로 리팩터링한다. 테스트 케이스가 있으니 코드를 정리하며 시스템이 깨질 걱정을 할 필요는 없다.
비슷한 코드는 더 비슷하게 고쳐주면 리팩터링이 쉬워진다.
아래 예시에서는 두 메서드에서 일부 코드가 동일하다.
public void scaleToOneDimension(
float desiredDimension, float imageDimension) {
if (Math.abs(desiredDimension - imageDimension) < errorThreshold)
return;
float scalingFactor = desiredDimension / imageDimension;
scalingFactor = (float) (Math.floor(scalingFactor * 100) * 0.01f);
RenderedOp newImage = ImageUtilities.getScaledImage(
image, scalingFactor, scalingFactor);
image.dispose();
System.gc();
image = newImage;
}
public synchronized void rotate(int degrees) {
RenderedOp newImage = ImageUtilities.getRotatedImage(
image, degrees);
image.dispose();
System.gc();
image = newImage;
}
위 코드에서 동일한 코드를 정리해 중복을 제거한다.
public void scaleToOneDimension(
float desiredDimension, float imageDimension) {
if (Math.abs(desiredDimension - imageDimension) < errorThreshold)
return;
float scalingFactor = desiredDimension / imageDimension;
scalingFactor = (float) (Math.floor(scalingFactor * 100) * 0.01f);
replaceImage(ImageUtilities.getScaledImage(image, scalingFactor, scalingFactor));
}
public synchronized void rotate(int degrees) {
replaceImage(ImageUtilities.getScaledImage(image, degrees));
}
private void relplaceImage(RenderedOp newImage) {
image.dispose();
System.gc();
image = newImage;
}
중복 제거 후에는 클래스가 SRP를 위반한다. 그러므로 새로 만든 relplaceImage메서드를 다른 클래스로 옮긴다. 그러면 새 메서드의 가시성이 높아지고, 추가적인 추상화를 통해 다른 맥락에서 재사용 역시 가능하다. 이러한 소규모 재사용은 시스템 복잡도를 극적으로 줄여준다. 소규모 재사용을 제대로 익히면 대규모 재사용이 가능하다.
코드는 개발자의 의도를 분명히 표현해야 한다. 개발자가 코드를 명백하게 짤수록 다른 사람이 그 코드를 이해하기 쉬워지며, 결함이 줄어들고 유지보수 비용이 줄어든다.
중복 제거, 의도 표현, SRP 준수라는 기본 개념도 극단으로 치달으면 득보다 실이 많다.
함수와 클래스 크기는 작게 유지하면서, 시스템 크기도 작게 유지하는 것이 궁극적인 목표이다. 그러므로 클래스와 함수 수를 줄이는 작업도 중요하지만, 테스트 케이스를 만들고 중복을 제거하고 의도를 표현하는 작업이 더 중요하다.