[책] 클린코드 - 애자일 소프트웨어 장인 정신 (10, 11, 12장)
클린코드(w.로버트 C. 마틴)를 읽고 개인적인 학습을 위해 요약한 글 입니다. 문제 시 댓글 달아주시면 감사하겠습니다.
10. 클래스
클래스는 작아야 한다
함수와 마찬가지로 '작게'가 기본 규칙이다. 메서드의 수를 적게 하라는 것이 아니다. 책임을 줄여야한다. 작명은 클래스 크기를 줄이는 첫번째 관문이다. 간결한 이름이 떠오르지 않는다면 클래스의 크기가 너무 커서이다. 클래스 이름이 모호하다면 클래스 책임이 너무 많아서다.
단일 책임 원칙(Single Responsibility Principle)
클래스나 모듈을 변경할 이유가 하나뿐이어야 한다는 원칙. SRP는 '책임'의 개념을 정의하며 적절한 클래스 크기를 제시한다.
SRP는 중요한 개념이지만 가장 무시하는 규칙 중 하나이다. 그 이유는 "돌아가는 소프트웨어"에 초점을 맞추기 때문이다. "깨끗하고 체계적인 소프트웨어"와는 완전 별개다.
자잘한 단일 책임 클래스가 많으면 큰 그림이 어려워질까? 큰 클래스 속 돌아가는 부품이 많은 것 보다 차라리 낫다. 다만, 컴포넌트의 복잡성을 다루기 위한 체계적 정리는 필수이다.
작은 클래스는 각자 맡은 책임이 하나며, 변경할 이유가 하나며, 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.
응집도
응집도가 높다는 말은 클래스에 속한 메서드와 변수가 서로 의존하며 논리적인 단위로 묶인다는 의미이다. 클래스는 인스턴스 변수 수가 작아야한다. 일반적으로 메서드가 변수를 더 많이 사용할수록 메서드와 클래스는 응집도가 더 높다.
큰 함수를 작은 함수 여럿으로 나누기만 해도 클래스 수가 많아진다. 예를 들어 큰 함수에서 작은 함수를 빼내려 한다. 큰 함수에 정의된 변수를 사용한다. 이걸 인수로 넘겨야할까? 아니다. 클래스 인스턴스 변수로 승격하면 된다. 그럼 함수를 쪼개기 쉬워진다.
이렇게 한다면, 클래스의 응집력이 떨어진다. 몇몇 함수만 사용하는 인스턴스 변수가 늘기 때문이다. 그러면 클래스를 쪼개면 된다. 그러면서 프로그램에 점점 더 체계가 잡히고 구조가 투명해진다.
변경하기 쉬운 클래스
대다수 시스템은 지속적인 변경이 가해진다. 그때마다 위험이 따른다. 깨끗한 시스템은 클래스를 체계적으로 정리해 변경에 수반하는 위험을 낮춘다.
(P.189 코드 참조)
위와 같은 코드는 시스템 결합도를 낮추면 유연성과 재사용성도 높아진다. 이러면 코드에서 구체적인 사실을 숨길수도 있다.
11. 시스템 (다시 읽어보기)
시스템 제작과 사용을 분리하기
Main 분리 (P.196)
생성과 관련한 코드는 모두 main이나 main이 호출하는 모듈로 옮긴다. 나머지 시스템은 모든 객체가 생성되었고, 모든 의존성이 연결되었다고 가정.
애플리케이션은 main이나 객체가 생성되는 과정을 전혀 모른다는 뜻.
팩토리 (P.197)
객체가 생성되는 시점을 애플리케이션이 결정해야 할 필요가 있다면, ABSTRACT FACTORY 패턴을 사용한다.
의존성 주입
사용과 제작을 분리하는 강력한 메커니즘 하나가 의존성 주입이다. 책임을 질 메커니즘으로 main루틴이나 특수 컨테이너를 사용한다. 진정한 의존성 주입은 클래스가 의존성을 해결하려고 시도하지 않는다. 클래스는 완전 수동. 대신 의존성을 주입하는 방법으로 설정자 메서드나 생성자 인수를 제공한다.
확장
처음부터 올바르게는 미신이다. 반복적이고 점진적으로, 애자일 방식의 핵심이다. 테스트 주도 개발, 리팩터링, 깨끗한 코드는 코드 수준에서 시스템을 조정하고 확장하기 쉽게 만든다.
시스템 수준에서는? 관심사 분리를 하지 못하면, 규모 확장이 어렵다. 영속성과 같은 관심사는 애플리케이션의 자연스러운 객체 경계를 넘나드는 경향이 있다. 모든 객체가 전반적으로 동일한 방식을 이용하게 만들어야 한다.
AOP에서 관점이라는 모듈 구성 개념은 특정 관심사를 지원하려면 시스템에서 특정 지점들이 동작하는 방식을 일관성 있게 바꿔야 한다고 명시한다. 명시는 간결한 선언이나 프로그래밍 메커니즘으로 수행한다.
순수 자바 AOP 프레임워크
POJO는 순수하게 도메인에 초점을 맞춘다. 프레임워크에 의존하지 않는다. 따라서 테스트가 개념적으로 더 쉽고 간단하다. 상대적으로 단순하여 사용자 스토리를 구현하기 쉽고 코드를 보수하기 좋다.
12. 창발성
창발적 설계로 깔끔한 코드를 구현하자.
- 모든 테스트를 실행한다.
- 중복을 없앤다.
- 프로그래머 의도를 표현한다.
- 클래스와 메서드 수를 최소로 줄인다.
모든 테스트를 실행한다
결합도가 높으면 테스트 케이스를 작성하기 어렵다. 그러므로, 테스트 케이스를 많이 작성할수록 개발자는 DIP와 같은 원칙을 적용하고 의존성 주입, 인터페이스, 추상화 등과 같은 도구를 사용해 결합도를 낮춘다. 따라서 설계 품질은 더욱 높아진다.
리팩터링
코드를 점진적으로 리팩터링 해나간다. 걱정할 필요 없다. 테스트 케이스가 있으니까.
중복을 없애라
P.220 코드 참고
표현하라
코드는 개발자의 의도를 명확히 표현해야한다. 그래야 결함이 줄고, 유지보수 비용이 적게 든다.
- 좋은 이름
- 함수와 클래스 크기 줄이기
- 표준 명칭
- 단위 테스트 케이스를 꼼꼼히 작성하기