점진적인 개선
출발을 좋았으나 확장성이 부족했던 모듈을 소개하고 개선하는 단계로 살펴본다.
.....
이름을 붙인 방법, 함수 크기, 코드 형식
프로그램을 처음부터 잘 짜기란 쉽지 않다.
또한 대부분이 그럴 수 없다.
프로그래밍은 과학보다는 공예에 가깝다. 깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다.
글을 쓸 때도 초안 -> 수정 -> 수정 -> 최종안 을 거쳐가 듯, 코드도 단계적으로 개선해야 한다.
대다수의 신참 프로그래머는 해당 단계를 무시하거나 충실히 따르지 않는다.
그들은 돌아가는 프로그램을 목표로 잡는다. 일단 돌아가면 방치한다.
1차 초안
이 표현은 낯 뜨거운 표현이다.
결국 미완성이다.
누구든 첫 버전부터 엉망이진 않다. 버전을 거치며 구현하는 기능이 많아지고 코드가 길어지는 등의 단계를 거듭할수록
코드는 점점 내 손을 벗어난다. (그래서 작게작게 만드는 거??)
최종 목표가 모든 자료형 타입의 인자를 받는 다고 가정하자.
버전 1. boolean 타입만 구현
코드가 그리 엉망은 아니다
버전 2. String 타입도 구현
코드가 점점 길어지며, setBoolean, setString 등의 같은 기능을 하는 중복 메소드가 생긴다.
-> 책에서는 class 상속을 이용하여 중복 메소드를 하나로 줄였다.
버전 3. Int, Double 등 구현
class 상속을 이용하지 않고 별도로 구현시, 사용하는 프로퍼티/메소드 등 쓸데없는 중복이 많을 것이다.
그래서 멈췄다.
버전 2를 마친 후, 코드를 더 작성해서는 안된다.
-> 아직 추가할 타입이 2개나 남았는데, 벌써 코드가 이렇다고? 그 2개를 더 작성하면 어떻게 될지 눈에 훤하다. 그런데도 계속 작성할 것인가?
-> 물론 밀어붙이면 어떻게든 돌아가는 프로그램을 완성했겠지만
-> 그 이후가 문제다. 어떻게 유지보수할 것인가?
코드 구조를 좋은 상태로 만들기 위한 일시정지
- 기능을 더 이상 추가하지 않고 리팩터링 시작
- String, Integer 인수 타입을 추가한 경험을 통해, 새로운 인수 타입을 추가하려면 주요 지점 세 곳에다 코드를 추가해야 한다는 것을 겪었다.
- HashMap을 선택하기 위해 스키마 요소의 구문을 분석
- 인수 유형을 분석해 진짜 유형으로 변환
- getXXX 메서드를 구현해 호출자에게 진짜 유형을 반환
- 인수 유형을 다양하지만 모두가 유사한 메서드를 제공하므로 클래스 하나가 적합하다고 판단하여 ArgumentMarshaler Abstract Class를 작성
점진적으로 개선하다
프로그램을 망치는 가장 좋은 방법 중 하나는 개선이라는 이름 하에 구조를 뒤집는 행위다
-> 프로그램을 '개선' 전과 같이 돌리기가 어렵다.
-> TDD를 통해 작성되어 있던 TC를 이용할 수 있다.
책에서는 Args Class를 구현하기 이전에 이미 단위테스트와 인수테스트를 만들었다.
그리고 해당 테스트를 모두 통과하면 올바로 동작한다고 봐도 무방했다.
순차적인 개선
기존에 변경되어야 하는 곳(parse, get, set) 메소드를 단번에 수정하지 않는다.
- ArgumentMarshaler Abstract Class가 아닌 ArgumentMarshaler Class의 골격을 추가하여 코드를 최소로 건드리는 변경만 가한다.
- 해당 변경으로 꺠지는 코드를 수정
- 수정 후에 테스트케이스를 모두 통과하는지 확인해야 한다.
- 단번에 많은 코드를 수정하고 테스트를 돌리면 어디가 잘못되었는지 알 수 없다.
- 구현할 인수 타입들을 모두 구현한 뒤, ArgumentMarshaler 를 추상 클래스로 선언하고 타입마다 XXXArgumentMarshaler Class를 선언
- get/set 메소드를 추상 메소드로 선언
- Integer/String 에는 Iterator가 필요하지만 Boolean에는 필요가 없었지만, ArgumentMarshaler에서 모두 처리하기 위해, Iterator를 매개변수로 갖는 추상 메소드를 선언
- Args Class에서 던지는 예외는 Args와 관련이 있는 것
- Integer관련 Exception은 IntegerArgumentMarshaler Class에서, String은 StringArgumentMarshaler에서 던진 후, Args와 같은 호출한 곳에서 Args와 관련있는 Exception으로 치환 혹은 생성하여 throw
버전 1에서부터 점진적으로 리팩토링을 진행하면서
- 주로 Args Class에서 코드를 삭제했다.
- 삭제된 코드는 ArgsException Class, ArgumentMarshaler Class로 옮겨졌다.
결론
그저 돌아가는 코드만으로는 부족하다. 돌아가는 코드가 심하게 망가지는 사례가 흔하기 때문이다.
설계와 구조를 개선할 시간이 없다고 변명할 지 모르지만 동의할 수 없다.
나쁜 코드보다 프로젝트에 악영향을 미치는 것은 없다
- 나쁜 요구사항
- 다시 정의하면 된다
- 나쁜 팀 역학
- 복구하면 된다
- 나쁜 코드
- 썩어 문드러진다.
- 팀의 발목을 잡는다.
처음부터 코드를 깨끗하게 유지하기란 상대적으로 쉽다. 아침에 엉망으로 만든 코드를 오후에 정리하기는 어렵지 않다.
그러므로 코드는 언제나 최대한 깔끔하고 단순하게 정리하자.
'스터디 > 클린코드' 카테고리의 다른 글
[Clean Code] 클린 코드 (냄새와 휴리스틱) - 20 (0) | 2021.03.29 |
---|---|
[Clean Code] 클린 코드(동시성) - 18 (0) | 2021.03.10 |
[Clean Code] 클린 코드(창발성) - 17 (0) | 2021.03.10 |
[Clean Code] 클린 코드(시스템) - 16 (0) | 2021.03.10 |
[Clean Code] 클린 코드(클래스) - 15 (0) | 2021.03.09 |