•
보통, 신규 기능을 메소드를 추가하는 식으로 아주 손쉽게 변경할 수 있다.
◦
그러나 이런 행위가 반복되면 거대해진 클래스를 마주하게 된다.
•
클래스가 커지면 어떤 문제가 생길까?
◦
첫 번째는 혼란이다. 수많은 메소드는 무엇을 변경해야 할지, 그리고 다른 클래스에 어떤 영향을 줄지 파악하는 것을 어렵게 만든다.
◦
두 번째는 작업 계획의 조정이다. 한 개의 클래스가 20개 정도의 책임을 가지고 있으면 그 클래스를 변경하는 이유도 20개 이상이다. 병렬적으로 프로그래밍하기가 어려워진다.
◦
세 번째는 테스트 코드 작성의 어려움이다.
•
거대한 클래스를 다뤄야할 때의 첫 과제는 상황을 악화시키지 않는 것이다.
◦
우리는 이를 위해 발아 클래스, 메소드 기법을 사용할 수 있다.
◦
변경이 필요할 때, 신규 클래스나 메소드로서 추가하는 것을 우선적으로 고려하라는 의미다.
•
거대한 클래스를 개선하는 핵심은 리팩토링이다. 리팩토링은 작은 클래스들로 쪼개는데 도움을 주며 이 설계를 위한 다양한 원칙들 기반으로 선택에 도움을 줄 수 있다.
◦
단일 책임 원칙
책임 파악
•
저자의 생가에 책임을 파악할 때의 주요한 질문은 다음과 같으며 저자는 이를 메소드 그룹화라고 부른다.
◦
왜 이 메소드가 여기 있을까?
◦
이 메소드는 클래스를 위해 무슨 일을 할까?
•
해당 기법 외에도 기존 코드의 책임을 찾기 위해 다음의 휴리스틱을 참고하면 도움이 된다.
◦
메소드들을 분류한다.
▪
이름이 비슷한 메소드들을 찾는다. 이는 책임 파악을 시작할 때 매우 좋은 기법이다.
◦
숨겨진 메소드들을 조사한다.
▪
private과 protected 메소드에 주의하며 이들의 수가 과하다고 판단된다면 별도의 클래스로 추출해내야 한다.
◦
변경 가능한 결정 사항을 찾아라.
▪
이미 결정이 끝난 사항 중 변경이 가능한 부분들을 찾자.
▪
이들을 추상화시킨다면 인터페이스로 추출하여 의존 관계를 제거할 수 있다.
◦
내부 관계들을 찾아낸다.
▪
인스턴스 변수와 메소드 사이의 관계를 찾는다.
▪
특정 메소드에서만 사용되는 인스턴스 변수가 굳이 인스턴스 변수여야 할 필요가 있을까?
▪
이 방법은 스케치를 사용해 그룹화하는 식으로 구현해보면 쉽게 파악할 수 있다.
◦
주요 책임을 찾는다.
▪
클래스의 책임을 하나의 문장으로 설명가능하도록 노력한다.
▪
단일 책임 원칙에 대한 위반은 보통 두 가지 방식으로 일어난다.
•
하나는 애초에 명시부터 책임 두 개 이상 갖는 것이고 다른 하나는 구현 과정에서 책임을 두 개 이상 갖는 것이다.
•
가장 조심해야할 것은 구현 과정에서의 위반이다.
◦
다른 모든 방법이 실패한다면, 스크래치 리팩토링을 수행한다.
◦
현재 작업에 집중한다.
▪
우리의 요구사항을 충분히 이해하는 것만으로도 신규 코드의 책임과 기존 코드의 책임을 분리할 수 있다.
그 밖의 기법들
•
책임 식별을 잘하는 방법은 다양한 문서를 많이 읽어보는 것이다.
•
다른 사람들의 코드를 읽어보며 명명법, 클래스 내부의 메소드 명의 관련성에 주목한다.
•
이런 연습을 통해 책임을 발견하는, 정의하는 능력을 기를 수 있다.
더 나아가기
•
책임들을 식별했다면, 문제를 정의했다면 다음은 해결이다. 즉, 전략과 전술이다.
전략
•
거대한 클래스들을 식별한 후 나누는 가장 좋은 접근법은 책임을 식별하고 팀원들에게 책임을 이해시킨 후, 필요에 따라 클래스를 분할하는 것이다.
•
변경으로 인한 위험을 분산시키고 동시에 다른 작업도 수행할 수 있다.
전술
•
대부분의 레거시 시스템은 메소드 시그니처를 변경하는 것이 위험성이 높기 때문에, 구현 수준에서 단일 책임 원칙을 적용하는 정도가 처음 시도해볼 수 있는 방안이다.
◦
이렇게 구현에서 책임들을 정리하고 나면 인터페이스 수준에서 책임 정리가 시작된다.
•
구현 수준에서는 클래스 추출 기법을 위해 다양한 요인을 고려할 수 있는데, 그중 하나가 영향을 받는 메소드에 대한 테스트 루틴을 얼마나 쉽게 작성할 수 있느냐다.
◦
클래스를 조사한 후, 이동해야 할 인스턴스 변수와 메소드를 모두 나열하는 것이 좋다.
•
이렇게 테스트 루틴을 작성하면 클래스를 쉽게 추출해낼 수 있다.
클래스 추출을 마친 후
•
개선을 위해서는 이미 존재하고 있는 것들을 충분히 고려하고, 반드시 이상적인 설계는 아닐지라도 최소한 지금보다 더 나은 방향을 바라봐야 한다.