Today I../Today I Read

[Clean code] Chapter 12. Emergence (창발적 설계로 깔끔한 코드 구현하기)

HJChung 2022. 3. 6. 21:55
Clean Code 클린 코드 - 로버트 C. 마틴 저
를 읽고, clean code 해설 강의를 통해 제가 이해한 바를 정리한 글입니다. 

 

창발성이란

이 책에서 창발성이란 단어를 처음 접했다. 

위키를 찾아보면 

창발(創發)또는 떠오름 현상은 하위 계층(구성 요소)에는 없는 특성이나 행동이 상위 계층(전체 구조)에서 자발적으로 돌연히 출현하는 현상이다. 

이라고 적혀있다. 

즉, 작은 요소들의 상호작용의 반복이 전체 구조에 영향을 미치는 점을 창발성이라고 한다. 

이번 챕터에서는 개발에서도 단순한 4가지 규칙을 반복하다 보면 Clean code, 우수한 설계가 나오게 된다고 하며 4가지 규칙을 소개한다. 

 

1. 모든 테스트를 실행하라. 

테스트의 중요성은 몇 번 반복해서 나왔다. [Clean code] Chapter 09. Unit test 에서

테스트 코드의 중요성을 

  • 테스트 코드는 실수를 바로 잡아준다.
  • 코드에 유연성, 유지보수성,  재사용성을 제공하는 버팀목이 단위 테스트이다.
  • 테스트는 실사용에 적합한 설계를 끌어내 준다. 
  • 테스트를 작성해서 얻게 되는 가장 큰 수확은 테스트 자체가 아니다. 작성 과정에서 얻는 깨달음이다. 

이라고 정리한 바 있다. 이번 챕터에서도 비슷하게 테스트는 서비스를 검증해주는 역할도 하지만 테스트를 짜면서 결합도가 높은 코드의 테스트를 짜기는 어렵기 때문에 테스트를 짜는 과정에서 낮은 결합도와 높은 응집력이라는 설계 목표를 달성할 수 있을 것이기 때문에 중요하다고 언급한다. 

 

2. 중복을 없앤다. 

중복을 없애는 방법은 기존의 코드를 최대한 재활용하는 방법, 중복된 코드를 별도의 메서드로 분리하는 방법 등이 있다. 

그리고 Template Method 패턴은 고차원 중복을 제거할 목적으로 자주 사용되는 방법이다. 

Template Method Pattern

실무에서 머신러닝 코드를 리펙토링 해야 하는 일이 있었는데, 그때 코드 중복을 어떻게 하면 줄 일 수 있을까 생각하다 찾은 패턴이 Template Method 패턴이었다. 

일반적으로 생각해보았을 때도 머신러닝 모델의 경우 일정한 코드의 실행 순서(model load -> analyze -> post process -> save result)가 있다. 이처럼 구현하려는 알고리즘에 일정한 단계가 있고, 세부 단계마다 조금씩 구현 내용이 다를 때 Template Method Design pattern을 사용할 수 있다. 

 

정리하면 Template Method Design pattern 이란 알고리즘의 여러 단계를 각 메서드로 선언하고, 그 알고리즘을 수행할 템플릿 메서드를 만든다. 그리고 하위 클래스에서는 여러 단계에 해당하는 메서드의 세부사항을 구현하는 것이다. 그리고 이 패턴은 아래의 경우에 사용하기 좋다. 

  • 구현하려는 알고리즘이 일정한 프로세스가 있다. ( =여러 단계가 있다.)
  • 구현하려는 알고리즘이 변경 가능성이 있다.

사용 예

Doc, CSV, PDF 파일을 분석해야 하는 경우가 있을 때

데이터 형식을 처리하는 코드는 각기 다르지만 데이터 처리와 분석을 담당하는 코드는 거의 동일하다고 가정.

이때 Template Method pattern을 사용하면 전체 알고리즘을 각 단계로 나누고 이 단계는 각 method로 구현한다. 그리고 각 step에 해당하는 method들이 template method에서 serial 하게 실행된다.

그럼 class 마다 뭐가 다른지 파악해서

  • abstract steps must be implemented by every subclass
  • optional steps already have some default implementation, but still can be overridden if needed
  • hooks steps A template method would work even if a hook isn’t overridden. Usually, hooks are placed before and after crucial steps of algorithms, providing subclasses with additional extension points for an algorithm.

적용 방법

  1. 알고리즘을 여러 단계로 나눈다.
  2. 나눠진 알고리즘의 단계를 메서드로 선언한다.
  3. 알고리즘을 수행할 템플릿 메서드를 만든다.
  4. 하위 클래스에서 나눠진 메서드들을 구현한다.

Pros and Cons

장점

  •  You can let clients override only certain parts of a large algorithm, making them less affected by changes that happen to other parts of the algorithm.
  •  You can pull the duplicate code into a superclass.

단점

  •  Some clients may be limited by the provided skeleton of an algorithm.
  •  You might violate the Liskov Substitution Principle by suppressing a default step implementation via a subclass.
  •  Template methods tend to be harder to maintain the more steps they have

 

3.  프로그래머의 의도를 표현하라. 

SW 프로젝트 비용 중 대다수는 장기적인 유지보수에 들어간다. 그래서 유지보수 개발자는 시스템을 제대로 이해해야 한다.

그리고 또 해당 SW를 개발한 개발자가 퇴사한다거나 해서 유지보수 시 물어볼 수 없다면?!!🥶

 

그러므로 코드는 개발자의 의도를 분명히 표현하고 있어야 하고 그래야 결함도 줄고 유지보수 비용이 적게 든다. 

  1. 좋은 이름을 선택하자. [Clean Code] Chapter 02. 의미 있는 이름
  2. 함수와 클래스 크기를 가능한 줄이자. 
  3. 표준 명칭을 사용하자. 디자인 패턴을 사용했다면 그 이름을 클래스에 넣어주어 개발자가 패턴을 이름을 통해서도 바로 알 수 있도록 하자. 
  4. 단위 테스트 케이스를 꼼꼼하게 작성하자. 테스트 케이스는 코드를 이해하는 예시가 된다. 
  5. 그리고 주의 깊고, 사려 깊게 작성하고자 하는 마음가짐! 이 중요하다. 

 

4. 클래스와 메서드 수를 최소로 줄이자. 

이 부분은 책을 통해서는 잘 와닿지가 않았는데, 해설 강의를 보니 '실용적 관점에서 타협하자'라는 의미로 이해가 잘 되었다. 

좋은 코드를 만드는 이유는 생산성을 올리기 위해서이다. 그런데 클린 코드를 읽고 배운 것들을 다 적용하려면 굳이 그럴 필요 없는 데도 많은 인터페이스, 클래스, 메서드.. 들이 생길 수 있다. 

거의 발생하지 않을 일(ex. 향후 몇 년간 확장에 유연할 필요가 없는 서비스라던지)에 미리 투자하는 비용도 생각을 해야 한다. 

중요한 것은 적당한 미래(~1년)까지 대응할 수 있을 정도의 지금 시점에서 적절한 설계를 하는 것이다. 그리고 예상치 못한 변경이나 추가사항이 발생한다면? 그때 확장하는 설계를 해 나가면 된다. 

 

하지만 이는 많은 경험치를 통한 감도 어느 정도 있어야 할 것 같다는 생각이 든다. 하지만 이런 감이 부족해 조금 부족한 설계를 했더라도 규칙 1~3에 해당하는 것만 잘 지켰다면 확장을 위한 재설계의 부담은 많이 줄 것이라 생각한다. 

 

Reference