Skip to main content

3장 코드에서 나는 악취 下

written by
Woongjo-Yoo
Woongjo-Yoo 🏆Back End Engineer

자세한 해결 방법은 나중에 나오게 되니 지금은 우선 어떤 경우에서 악취가 나는지를 중점적으로 알아보면 좋을 것 같다.

3.13. 반복문 Loops#

자바스크립트는 일급 함수를 지원

반복문을 파이프라인으로 바꾸자.

filter, map 같은 파이프라인 연산을 이용해 각 원소들이 어떻게 처리되는지 명확하게 나타내자.

3.14 성의 없는 요소 Lazy Elements#

프로그래밍 언어가 제공하는 함수(메서드), 클래스, 인터페이스 등의 프로그래밍 요소를 이용하여 코드의 구조를 잡는 과정에서,

필요없는 것들은 제거하자

함수 인라인하기, 클래스 인라인하기, 계층 합치기 등을 적용하자.

3.15 추측성 일반화 Speculative Generality#

한마디로 미리 예측하고 추측하는 행위를 최소화하자!

YAGNI! 나중에 필요할거야 라는 생각으로 당장 필요 없는 것을 만들었다면 제거하자

3.16 임시 필드 Temporary Field#

특정 상황에서만 값이 설정되는 필드(임시 필드)를 가진 클래스가 있다면? -> 나중에 그 필드의 존재 여부, 이유 등을 파악하는데 고통스럽게 된다.

임시 필드와 관련된 코드는 별도의 클래스, 함수로 분리하여 관리하도록 한다.

3.17 메시지 체인#

클라이언트가 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 경우 -> 객체 내비게이션 구조에 종속되어 내비게이션 중간 단계를 수정하면 클라이언트 코드도 수정되는 상황이 벌어진다.

client.getSomething().getSomethingElse().getAnotherThing();client.manager.something.anotherThing.result;

위임 숨기기, 함수 추출하기, 함수 옮기기 등을 이용해 해결해보도록 한다(추후에 자세히 기술)

간단한 예시

// 위임 숨기기// 아래와 같은 체인을managerName = aPerson.department.manager.name;// 이런식으로 숨길 수 있게 된다managerName = aPerson.department.managerName            || aPerson.manager.name            || aPerson.managerName
// 결과적으로...const managerName = aPerson.department.manager.name;const report = `${managerName}${aPerson.name} 님의 작업 로그...`;console.log(report);
// 위와 같은 코드를 위임 숨기기와 약간의 모듈링만 하게 되면 아래처럼 바꿀 수 있다.console.log(reportAutoGenerator.report(aPerson));

3.18 중재자 Middle Man#

캡슐화와 위임으로 대표되는 기능 중 하나

팀장에게 미팅을 요청한 경우 우리가 원하는 것은 yes or no. 팀장이 다이어리를 살펴보든 다른 프로그램을 쓰던 비서를 두던 우리는 신경 안 써도 됨

하나의 객체가 어떤 결과를 받을 때, 그 사이에서 중재자를 활용하게 되면 비슷한 효과를 얻을 수 있게 된다.

그러나! 만약 중재자가 하는 일 중 절반 이상이 다른 클래스에 구현을 위임하고 있다면? -> 오히려 중재자를 제거하여 실제로 일을 하는 두 객체가 직접 만날 수 있도록 하자

3.19 내부자 거래 Insider Trading#

모듈들 사이에 결합도가 너무 높아지게 되는 것 같다면,

함수 옮기기, 필드 옮기기 등을 이용해서 클래스들끼리 사적으로 처리하는 부분을 줄인다.

여러 모듈이 같은 관심사를 공유하게 된다면,

정식으로 그 부분을 처리하는 제3의 모듈을 만들거나 위임 숨기기를 이용하여 중간자를 만든다.

상속 관계에서 자식 클래스가 부모 클래스에 대해 너무 많이 알아야된다면, 위임으로 바꾸도록 한다.

3.20 거대한 클래스 Large Class#

하나의 클래스가 너무 많은 일을 하려다보면 필드 수가 늘어나게 되고, 중복 코드도 늘어나기 쉽다.

이럴 땐 같은 prefix, suffix 를 가진 필드를 따로 추출하여 분리된 컴포넌트로 만든다.

컴포넌트를 분리할 때 그 특성에 따라 super 클래스로 만들거나 sub 클래스로 만들 수도 있다.

코드량이 너무 많은 클래스가 있다면 중복 코드가 많을 가능성이 높다.

그럴 경우, 클래스 내부에서 자체적으로 중복 코드를 제거하거나, 공통 부분을 작은 메서드들로 뽑아내는 등의 방법을 통해 최대한 작게 줄여본다.

마지막으로 클라이언트가 해당 객체를 어떻게 사용하는지를 파악하여, 주요 사용되는 기능별로 구분하여 개별 클래스로 추출할 수도 있ㅈ다.

3.21 서로 다른 인터페이스의 대안 클래스들 Alternative Classes with Different Interfaces#

클래스를 사용하게 되면 필요에 따라 언제든 다른 클래스(대안)으로 교체할 수가 있다.

단, 인터페이스 같아야하기 때문에 메서드 시그니처를 일치시키고, 필요한 동작들을 위해 함수를 메서드 안으로 옮기면서 대안 클래스를 만들고, 때로는 대안 클래스들을 위한 슈퍼클래스를 생성하도록 한다.

3.22 데이터 클래스 Data Class#

데이터 클래스란, 데이터 필드와 Getter/Setter로만 이루어진 클래스를 말한다.

단순 저장 용도로만 활용하다버니 다른 클래스가 너무 깊이까지 다룰 때가 많다.

이런 클래스에서는 레코드 캡슐화하기로 public 필드를 최대한 숨기고, 변경하면 안 되는 필드는 setter 를 제거하도록 한다.

3.23 상속 포기 Refused Bequest#

상속 받은 클래스지만 부모의 유산을 원치 않는 상황에서 보통 옛날에는 계층 구조를 잘못 설계했다고 보았다.

같은 계층에 클래스를 하나 더 만들고 물려받지 않을 부모 코드를 모조리 새로 만든 클래스로 옮긴다. 그러면 부모에는 공통된 부분만 남게 된다.

이러한 방법 vs 상속 포기 둘 중 선택을 할 수 있는데, 위와 같은 방법이 비록 냄새를 풍기기는 하지만 참을 수 있는 정도의 수준이라면 위의 방법대로 해도 문제는 없다.

반면 상속 포기는 서브 클래스가 부모의 동작은 필요로하지만 인터페이스는 따르고 싶지 않을 때 사용하면 좋다.

서브클래스 혹은 슈퍼클래스를 위임으로 바꾸어 상속 매커니즘에서 벗어나도록 만든다.

3.24 주석 Comments#

주석이 장황하게 달렸다면 코드를 잘못 작성했기 때문일 경우가 많다.

특정 코드 블럭이 하는 일에 주석을 남기고 싶다면 함수 추출하기를 적용해보고,

이미 추출한 함수임에도 여전히 설명이 필요하다면 함수 선언 바꾸기를 사용해본다.

어떤 동작을 위한 선행조건을 명시하고 싶다면 어서션 추가하기를 사용해본다.

주석을 사용해야 하는 경우는,

  • 현재 진행 상황
  • 확실하지 않은 상황에 대해서
  • 이렇게 코드를 작성한 이유