Clean Code를 읽고 내용을 정리한 것입니다.
창발성 (創發性)
하위 체계로부터 생겨나지만, 그 하위 체계로 환원되지 않는 속성
하위 계층(구성 요소)에는 없는 특성이나 행동이 상위 계층(전체 구조)에서 자발적으로 돌연히 출현하는 현상
조직(organization)의 일정수준에서 실체에 속한 성질은 그보다 낮은 차원에서 발견된 성질로부터는 예견할 수 없다는 것
즉, 하위 요소를 잘 결합하여 전혀 다른 결과를 얻어 내는 것을 말한다.
SW 설계 품질을 높여주는 켄트 벡의 단순한 설계 규칙 4가지 (중요도 순)
- 모든 테스트 실행
- 중복 제거
- 개발자의 의도를 표현
- 클래스/메소드의 수를 최소한으로
모든 테스트 실행하기
SW 설계는 개발자의 의도가 담겨있다.
테스트를 거친 시스템은 개발자의 의도를 검증한 개발 산출물이다.
테스트가 가능한 시스템을 만드려고 노력하면 설계의 품질도 높아질 수밖에 없다.
이유를 나열해보면 아래와 같다.
- 단일 책임 원칙을 지키는 클래스가 테스트하기 편하다.
- 테스트 케이스가 많을수록 개발자는 코드를 작성하기 쉬워진다.
- 결합도가 높으면 테스트를 작성하기 어렵다. 의존 관계 역전 법칙을 준수하고 추상화 기법과 같은 방법을 활용하면 결합도를 낮출 수 있다.
따라서 테스트를 철저히 수행할 수 있는 시스템은 곧 좋은 품질의 설계로 이어진다.
Refactoring
코드와 테스트 케이스까지 모두 작성을 완료했으면 정리를 할 타이밍이다.
테스트 케이스가 작성되어 있기 때문에 코드 변경을 두려워하지 않아도 된다.
리팩토링 단계에선 SW 설계 품질을 충분히 끌어올려야 한다.
- 응집도를 높인다.
- 결합도를 낮춘다.
- 관심사를 분리한다.
- 관심사를 모듈로 나눈다.
- 클래스/메소드 크기를 줄인다.
- 변수/메소드의 이름을 더 나은 것으로 교체한다.
중복 제거하기
중복은 SW 설계에 있어 가장 큰 적이다.
public void scaleToOneDimension(args...) {
... // 생략
Image newImage = ImageUtils.getScaledImage(image, scalingFactor, scalingFactor);
image.dispose();
System.gc();
image = newImage;
}
public synchronized void rotate(int degrees) {
Image newImage = ImageUtils.getScaledImage(image, degrees);
image.dispose();
System.gc();
image = newImage;
}
코드를 작성하고 보니 일부가 중복이다.
정리하여 중복을 제거한다.
public void scaleToOneDimension(args...) {
... // 생략
replaceImage(ImageUtils.getScaledImage(image, scalingFactor, scalingFactor));
}
public synchronized void rotate(int degrees) {
replaceImage(ImageUtils.getScaledImage(image, degrees));
}
private void replaceImage(Image newImage) {
image.dispose();
System.gc();
image = newImage;
}
중복을 제거했더니 조금 보기 좋아졌다.
하지만 이 클래스를 수정해야 할 이유가 이미지 스케일링, 회전에 이미지 대체까지 생겨버리며 단일 책임 원칙을 제대로 위반해버렸다.
replaceImage 메소드를 옮겨보자.
public class ImageReplacer {
private Image image;
public ImageReplacer() {}
public static ImageReplacer builder() {
return new ImageReplacer();
}
public ImageReplacer image(Image image) {
this.image = image;
return this;
}
public Image replace() {
return image;
}
}
public void scaleToOneDimension(args...) {
... // 생략
image = ImageReplacer.builder()
.image(ImageUtils.getScaledImage(image, scalingFactor, scalingFactor))
.replace();
}
public synchronized void rotate(int degrees) {
image = ImageReplacer.builder()
.image(ImageUtils.getScaledImage(image, degrees))
.replace();
}
ImageReplacer
는 Builder 패턴을 유사하게 사용하여 image를 반환한다.
이제 이미지 변환의 책임은 ImageReplacer
이 갖게 된다.
Template Method 패턴
Template Method 패턴은 중복 제거에 자주 사용되는 기법이다.
예제를 살펴보자.
public class SalaryPolicy {
public Salary calculateDeveloperSalary() {
// 대충 경력을 반영한다는 내용
// 대충 개발한 프로그램의 산출물 평가를 반영한다는 내용
// 대충 팀에서의 기여도를 수치화한다는 내용
}
public Salary calculateDesignerSalary() {
// 대충 경력을 반영한다는 내용
// 대충 디자인 산출물을 평가한다는 내용
// 대충 팀에서의 기여도를 수치화한다는 내용
}
}
위의 두 메소드는 개발 프로그램의 성과/디자인 만족도 평가 로직을 제외하면 동일하다.
자신이 맡은 역할에 따라 업무 평가 시스템이 약간 다를 뿐이다.
여기에 Template Method 패턴을 적용시켜보자.
public abstract class SalaryPolicy {
public Salary calculateSalary() {
reflectLongCareer();
reflectOutputEvaluation();
reflectContribution();
}
private void reflectLongCareer() { ... }
abstract protected void reflectOutputEvaluation();
private void reflectContribution() { ... }
}
public class DeveloperSalaryPolicy extends SalaryPolicy {
@Override
public Salary reflectOutputEvaluation() { ... }
}
public class DesignerSalaryPolicy extends SalaryPolicy {
@Override
public Salary reflectOutputEvaluation() { ... }
}
상위 클래스의 abstract 메소드인 reflectOutputEvaluation만 채워 급여 체계를 완성했다.
그 외의 메소드는 상위 클래스에 의존적이다.
개발자의 의도를 표현하기
본인만 이해하는 코드는 짜기 쉽다.
다 같이 이해하는 좋은 코드를 짜는 몇 가지 규칙이 있다.
- 이해하기 쉬운 이름을 붙이자.
- 클래스와 메소드 크기를 가능한 줄인다.
- 업계에서 일반적으로 사용되는 명칭을 활용한다. Decorator 패턴을 활용한 경우 *Decorator로 붙인다거나 Queue인 경우 *Queue로 짓는 식이다.
- 단위 테스트 케이스를 꼼꼼히 작성한다. 테스트 케이스는 예제를 통해 코드를 검증하는 절차이다. 잘 만든 테스트 케이스는 클래스의 기능을 짐작하기 쉽게 만든다.
무엇보다 가장 중요한 것은 표현하고자 하는 노력이다.
나중에 읽을 사람을 위해 약간의 시간을 투자하자.
그 사람이 내가 될 수도 있다.
클래스와 메소드 수를 최소로 줄이기
정도를 알아라, 적당히 해라 같은 말이 있다.
무엇이든 끝까지 파헤치려는 노력은 보기 좋지만, 그 하나에만 매달리며 다른 것을 등한시한다면 득보다 실이 많을 수도 있다.
SW 설계도 마찬가지다.
중복을 제거하고, 의도를 잘 표현하고, 단일 책임 원칙을 잘 준수하면 당연히 좋지만,
정도를 넘어서서 클래수 수와 메소드 수가 셀 수 없을 만큼 많아지면 오히려 관리가 어려울 수도 있다.
그래서 저자는 '무조건 해라!'가 아니라 '가급적 최소로' 할 것을 권장한다.
클래스와 메소드 수를 줄이는 궁극적인 목적은 결국 효율적인 시스템을 개발하기 위해서라는 것을 새겨두자.
'IT 도서 > Clean Code' 카테고리의 다른 글
[Clean Code] 동시성 (0) | 2020.02.20 |
---|---|
[Clean Code] 시스템 (0) | 2020.02.16 |
[Clean Code] 클래스 (0) | 2020.02.16 |
[Clean Code] 단위 테스트 (0) | 2020.02.15 |
[Clean Code] 주석 (0) | 2020.02.14 |