단순한 규칙 4가지를 지키는 것만으로도 좋은 설계가 창발한다.
테스트가 가능한 시스템을 만들려면 SRP를 준수해야 한다. 결합도를 낮추고 응집력을 높이게 된다.
작은 중복도 제거하라. TEMPLATE METHOD 등 패턴이 여기서 발생한다.
좋은 이름, 작은 함수와 클래스, 표준 패턴 사용으로 의도를 명확히 하라.
SRP를 극단으로 적용하면 오히려 클래스가 너무 많아진다. 실용적 균형을 찾아라.
중복을 제거하다 보면 숨어있던 SRP 위반이 드러난다.
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.getRotatedImage(image, degrees)); } private void replaceImage(RenderedOp newImage) { image.dispose(); System.gc(); image = newImage; }
알고리즘 전체 구조는 고정, 구현 세부는 하위 클래스가 정의한다.
public class VacationPolicy { public void accrueUSDDivisionVacation() { // 지금까지 근무한 시간을 바탕으로 // 휴가 일수를 계산하는 코드 // ... // 미국 최소 법정 일수를 만족하는지 확인 // ... // 급여 대장에 적용하는 코드 // ... } public void accrueEUDivisionVacation() { // 지금까지 근무한 시간을 바탕으로 // 휴가 일수를 계산하는 코드 // ... // 유럽연합 최소 법정 일수를 만족하는지 확인 // ... // 급여 대장에 적용하는 코드 // ... } }
abstract public class VacationPolicy { public void accrueVacation() { calculateBaseVacationHours(); alterForLegalMinimums(); applyToPayroll(); } private void calculateBaseVacationHours() { /* ... */ } abstract protected void alterForLegalMinimums(); private void applyToPayroll() { /* ... */ } } public class USVacationPolicy extends VacationPolicy { @Override protected void alterForLegalMinimums() { // 미국 최소 법정 일수를 사용한다. } } public class EUVacationPolicy extends VacationPolicy { @Override protected void alterForLegalMinimums() { // 유럽연합 최소 법정 일수를 사용한다. } }
창발적 설계가 만들어지는 방식과 그 가치
복잡한 설계를 처음부터 완벽하게 만들 수 없다. 단순한 규칙을 지키면 좋은 설계가 자연스럽게 나타난다.
중복을 제거하다 보면 숨어있던 책임 문제가 드러난다.
Template Method, Strategy 등 많은 패턴이 중복 제거 과정에서 자연스럽게 등장한다.
클래스와 메서드를 최소화하되, 독단적인 SRP 적용으로 클래스 수가 폭발하지 않게 균형을 찾아라.