- 창발적 설계로 깔끔한 코드 구현
켄트 백의 네 가지 단순한 디자인 규칙
모든 테스트를 실행합니다.
중복을 제거하십시오.
프로그래머의 의도를 표현하십시오.
클래스와 메서드의 수를 최소화합니다.
단순한 설계 규칙 1: 모든 테스트 실행
- 설계는 의도한 대로 작동하는 시스템으로 나타나야 합니다.
- 철저한 테스트를 거쳐 항상 모든 테스트 사례를 통과하는 시스템, 즉 테스트 가능한 시스템디자인을 만들기 위한 노력은 디자인의 질을 높입니다.
- 그 결과 클래스가 작고 하나의 목적만 수행합니다.
- SRP 호환 클래스는 테스트하기가 훨씬 쉽습니다.
- 테스트 사례가 많을수록 개발자가 테스트 코드를 작성하기가 더 쉬워집니다.
- 결합도가 높으면 테스트 케이스 작성이 더 어려워집니다.
따라서 DIP와 같은 원칙을 적용하려고 하면 DI, 인터페이스 및 추상화와 같은 도구를 사용하여 결합을 줄이기 때문에 디자인의 품질이 향상됩니다.
- 그 결과 클래스가 작고 하나의 목적만 수행합니다.
단순한 설계 규칙 2-4: 리팩토링
- 테스트 사례를 모두 작성했으면 코드와 클래스를 구성할 수 있습니다.
특히 코드를 점진적으로 수정합니다. - 리팩토링 단계에서는 소프트웨어 디자인의 품질을 향상시키는 기술을 적용해도 괜찮습니다.
다양한 기술을 사용하여 응집력을 높이고, 결합을 줄이고, 관심사, 모듈 시스템 관심사를 분리하고, 함수 및 클래스 크기를 줄이고, 더 나은 이름을 선택하는 등의 작업을 수행합니다.
단순한 설계 규칙 2. 중복 제거
- 복제는 추가 작업, 추가 위험 및 불필요한 복잡성을 의미합니다.
- 물론 동일한 코드는 중복이고 구현 중복은 중복의 한 형태입니다.
int size() {}
boolean isEmpty() {}
각 메소드를 개별적으로 구현하는 방법이 있지만 isEmpty 메소드는 부울을 반환하고 size 메소드는 개수를 반환합니다.
그러나 isEmpty 메소드에서 size 메소드를 사용하면 코드를 중복해서 구현할 필요가 없습니다.
boolean isEmpty() {
return 0 == size();
}
깨끗한 시스템에는 몇 개의 중복 라인이라도 제거하려는 의지가 필요합니다.
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;
}
scaleTOOneDimension과 회전 방식을 보면 일부 코드가 동일합니다.
다음과 같이 코드를 정리하여 중복을 제거합니다.
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.getRotatedImage(image, degrees));
}
public synchronized void rotate(int degrees) {
replaceImage(ImageUtilities.getRotatedImage(image, degrees));
}
public void replaceImage(RenderedOp newImage) {
image.dispose();
System.gc();
image = newImage;
}
아주 적은 양의 일반 코드를 새로운 메서드로 추출한 후 클래스는 SRP를 위반합니다.
그래서 새로 만든 replaceImage 메소드를 다른 클래스로 옮기는 것이 좋을 것 같습니다.
다른 팀 구성원은 다른 컨텍스트에서 재사용 기회를 위해 이러한 새로운 방법을 추가로 추상화할 수 있습니다.
이 “작은 재사용”은 시스템 복잡성을 크게 줄입니다.
대규모 재사용은 소규모 재사용을 제대로 마스터한 경우에만 가능합니다.
아래에 템플릿 메서드 패턴고차 중복성을 재사용하기 위해 일반적으로 사용되는 기술입니다.
public class VacationPolicy {
public void accrueUSDivisionVacation() {
// 지금까지 근무한 시간을 바탕으로 휴가 일수를 계산하는 코드
// ...
// 휴가 일수가 미국 최소 버정 일수를 만족하는지 확인하는 코드
// ...
// 휴가 일수를 급여 대장에 적용하는 코드
// ...
}
public void accrueEUDivisionVacation() {
// 지금까지 근무한 시간을 바탕으로 휴가 일수를 계산하는 코드
// ...
// 휴가 일수가 유럽 연합 최소 법정 일수를 만족하는지 확인하는 코드
// ...
// 휴가 일수를 급여 대장에 적용하는 코드
// ...
}
}
법적 최소 일수를 계산하는 코드를 제외하면 두 방법은 거의 동일합니다.
법적 최소 일수를 계산하는 알고리즘은 근로자 유형에 따라 조금씩 다릅니다.
여기에서 Template Mathod 패턴이 적용되어 눈길을 끄는 중복성을 제거합니다.
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() {
// 유럽 연합 최소 법정 일수를 사용한다.
}
}
하위 클래스는 중복되지 않는 정보만 제공하여 accrueVacation 알고리즘에서 누락된 ‘구멍’을 채웁니다.
단순한 설계 규칙 3. 프로그래머의 의도를 표현하라
- 우리 대부분은 전에 지저분한 코드를 접해본 적이 있을 것입니다.
나는 우리 대부분이 지저분한 코드를 게시한 경험이 있다고 확신합니다.
- 이해하는 코드를 작성하기 쉽습니다.
- 코드를 작성하면서 문제를 파헤치고 코드의 구석구석을 이해하기 때문입니다.
- 그러나 앞으로 코드를 유지 관리할 사람은 코드를 작성한 사람만큼 문제를 깊이 이해하지 못할 것입니다.
- 이해하는 코드를 작성하기 쉽습니다.
- 모든 소프트웨어 프로젝트 비용의 대부분은 장기 유지 관리에 사용됩니다.
- 코드 변경으로 인해 버그가 발생하지 않도록 유지 관리 개발자는 시스템을 제대로 이해해야 합니다.
- 그러나 시스템이 복잡해짐에 따라 유지보수 개발자는 시스템을 이해하기 위해 점점 더 많은 시간을 소비하며 동시에 코드가 오해될 가능성이 높아집니다.
- 따라서 코드는 개발자의 의도를 명확하게 표현해야 합니다.
- 개발자가 작성하는 코드가 명확할수록 다른 사람들이 코드를 이해하기가 더 쉽습니다.
- 이것은 결함을 줄이고 유지 보수 비용을 낮춥니다.
- 코드 변경으로 인해 버그가 발생하지 않도록 유지 관리 개발자는 시스템을 제대로 이해해야 합니다.
- 좋은 이름을 선택하십시오. 완전히 다른 이름과 기능을 가진 클래스나 기능으로 관리자를 놀라게 하지 마십시오.
- 가능한 경우 함수 및 클래스 크기를 줄입니다.
작은 클래스와 작은 함수는 이름을 지정하기 쉽고 구현하기 쉽고 이해하기 쉽습니다. - 표준 명명법을 사용하십시오.
- 예를 들어 디자인 패턴은 주로 커뮤니케이션과 표현력을 높여야 합니다.
- 클래스가 COMMAND 또는 VISITOR와 같은 표준 패턴으로 구현된 경우 패턴 이름이 클래스 이름에 포함됩니다.
- 이렇게 하면 다른 개발자가 클래스 디자인의 의도를 더 쉽게 이해할 수 있습니다.
- 예를 들어 디자인 패턴은 주로 커뮤니케이션과 표현력을 높여야 합니다.
- 세심한 단위 테스트 사례를 작성하십시오.
- 테스트 케이스는 소위 “샘플 문서”입니다.
- 즉, 잘 만들어진 테스트 케이스를 읽으면 클래스의 기능을 한눈에 볼 수 있다.
- 그러나 표현력을 높이는 가장 중요한 방법은 노력입니다.
- 코드를 실행하고 바로 다음 문제로 넘어가는 것은 너무나 흔한 일입니다.
- 미래의 독자들이 조금 더 쉽게 읽을 수 있도록 충분한 생각을 찾기가 어렵습니다.
- 그러나 나중에 코드를 읽는 사람은 자신이 될 가능성이 높다는 점을 명심하십시오.
- 그럼 작업을 조금 더 보여드리겠습니다.
- 함수와 클래스에 더 많은 시간을 할애합시다.
더 나은 이름을 선택하고 큰 기능을 작은 기능으로 나누고 작업에 조금 더 주의를 기울이십시오. 주의력은 훌륭한 재능입니다.
- 테스트 케이스는 소위 “샘플 문서”입니다.
단순한 설계 규칙 4. 클래스 및 메서드 수 최소화
- 중복 제거, 의도 선언 및 SRP 준수와 같은 기본 개념도 극단적으로 사용하면 득보다 실이 더 많을 수 있습니다.
- 클래스와 메서드 크기를 줄이기 위해 작은 클래스와 메서드를 많이 만드는 것은 드문 일이 아닙니다.
- 따라서 이 규칙은 함수와 클래스의 수를 최대한 줄이는 것을 제안합니다.
때때로 무의미하고 자의적인 정책으로 인해 클래스와 메서드의 수가 증가합니다.
- 좋은 예는 각 클래스에 대한 무조건적인 인터페이스 생성을 요구하는 구현 표준입니다.
- 데이터 클래스와 액션 클래스를 분리해야 한다고 주장하는 개발자가 좋은 예입니다.
- 좋은 예는 각 클래스에 대한 무조건적인 인터페이스 생성을 요구하는 구현 표준입니다.
- 가능한 한 독단적인 견해를 피하고 실용적인 접근 방식을 선택하십시오.
- 목표는 시스템 크기를 작게 유지하면서 기능 및 클래스 크기를 작게 유지하는 것입니다.
- 그러나 이 규칙은 단순 디자인 규칙 중에서 우선 순위가 가장 낮습니다.
- 즉, 클래스와 함수의 수를 줄이는 것도 중요하지만 테스트 케이스를 만들고 중복을 제거하고 의도를 표현하는 작업더 중요하다는 뜻입니다.
- 목표는 시스템 크기를 작게 유지하면서 기능 및 클래스 크기를 작게 유지하는 것입니다.
참조 링크