리팩터링 2판 - 챕터03 스터디

Feb 13, 2025

코드에서 나는 악취

리팩터링을 하는 방법도 중요하지만 ‘제때 적용’할 줄 아는 것도 중요. 하지만 ‘언제’해야하는지 명확하게 정립된 규칙도 없음

챕터 3를 읽고 코드가 풍기는 악취가 무엇인지 찾아보자.

기이한 이름(Mysterious name)

코드는 단순하고 명료하게 작성해야한다. 그 중 가장 중요한 요소는 ‘이름’

함수, 모듈, 변수, 클래스는 이름만 보고도 무슨 일을 하고 어떻게 사용해야 하는지 명확히 할 수 있도록 신경써 이름을 지어야함.

가장 많이 사용하는 리팩터링도 함수 선언, 변수 이름, 필드 이름 바꾸기처럼 이름을 바꾸는 리팩터링이며, 이름만 잘 짓더라도 문맥을 파악시 시간 절약 가능.

혼란스러운 이름을 정리하다보면 코드가 간결해질 때가 많음

중복 코드(Duplicated code)

똑같은 코드 구조가 여러 곳에서 반복된다면 하나로 통합하여 더 나은 프로그램을 만들 수 있음.

한 클래스에 두 메서드가 같은 표현식 사용한다면 → 함수 추출(Extract function)

코드가 비슷한데 완전히 똑같지 않다면 → 문장 슬라이드(Slide Statements)

같은 부모로부터 파생된 클래스들에 코드가 중복되어 있다면 각자 따로 호출되지 않도록 → 메서드 올리기(Pull Up Method)

긴 함수(Long Function)

오랜기간 잘 활용되는 프로그램들은 하나같이 짧은 함수로 구성된다.

얼핏보면 연산하는 부분이 하나도 없어보임 → 이는 코드가 끊임없이 위임하는 방식으로 짜여져 있기 때문

간접 호출(indirection)의 효과는 코드를 이해하고 공유하고 선택하기 쉬워진다는 장점은 함수를 짧게 구성할 때 나오는 것

함수가 길수록 이해하기 어려움 -> 과거엔 서브루틴을 호출하는 비용이 컸기 때문에 짧은 함수를 꺼림(하지만 요즘 언어는 프로세스 안에서의 함수 호출 비용을 거의 없애버림)

코드를 읽는 사람 입장에서는 함수가 하는 일을 파악하기 위해 왔다갔다 해야 하므로 부담이 될 수 있지만, 함수 호출부와 선언부 사이를 빨게 이동하거나 호출과 선언을 동시에 보여주는 개발 환경을 활용하면 이 부담이 줄어들며 짧은 함수로 구성된 코드를 이해하기 쉽게 만드는 가장 확실한 방법은 ‘좋은 이름’

적극적으로 함수를 쪼개야함. -> 주석을 달아야 할 만한 부분은 무조건 함수로 만듦

함수 추출하기 : 함수 본문에서 따로 묶어 빼내면 좋은 코드 덩어리를 찾아 새로운 함수로 만드는 것. 여기서 함수가 매개변수와 임시 변수를 많이 사용한다면 추출 작업에 방해가 됨

이런 상황에서 함수를 추출하다 보면 추출된 함수에도 매개변수가 너무 많아져서 리팩터링 전보다 난해해질 수 있음 이를 위해:

  • 임시 변수 처리: 임시변수를 질의 함수로 바꾸기
  • 매개변수 수 줄이기: 매개변수 객체 만들기, 객체 통째로 넘기기
  • 여전히 임시변수와 매개변수가 너무 많다면 함수를 명령으로 바꾸기 적용

주석은 코드만으로 이해하기 어려운 부분에 달려있음

이런 주석을 찾으면 주석이 설명하는 코드와 함께 함수로 빼내고 함수 이름은 주석 내용을 토대로 지음. 코드가 단 한줄이어도 따로 설명할 필요가 있다면 함수로 추출하는게 좋으며, 조건문이나 반복문도 추출 대상의 실마리를 제공함.

긴 매개변수 목록(Long Parameter List)

과거에는 함수에 필요한 것들을 모조리 매개변수로 전달했지만, 매개변수 목록이 길어지면 그 자체로 이해하기 어려울 때가 많음 -> 매개변수를 질의 함수로 바꾸기로 제거가능

사용중인 데이터 구조에서 값들을 뽑아 각각을 별개의 매개변수로 전달하는 코드 → 객체 통째로 넘기기

항상 함께 전달되는 매개변수들 → 매개변수 객체 만들기

함수의 동작 방식을 정하는 플래그 역할으 ㅣ매개변수 → 플래그 인수 제거하기

클래스는 매개변수 목록을 줄이는 데 효과적인 수단. 특히 여러 개의 함수가 특정 매개변수들의 값을 공통적으로 사용할 때 유용

여러 함수를 클래스로 묶고, 공통 값들을 클래스의 필드로 정의. 함수형 프로그래밍이었다면 일련의 부분 적용함수(partially applied function)들을 생성한다고 일컬을 것

전역 데이터(Global Data)

전역 데이터를 주의하라! 유령 같은 원격작용(spooky action at a distance)처럼, 버그가 끊임없이 발생하는데 그 원인이 되는 코드를 찾아내기 굉장히 힘듬

클래스 변수와 싱글톤(singleton)에서도 같은 문제가 발생 -> 이를 방지하기 위해 변수 캡슐화함.

데이터를 함수로 감싸는 것만으로도 데이터를 수정하는 부분을 쉽게 찾을 수 있고 접근을 통제할 수 있으며, 접근자 함수들을 클래스나 모듈에 집어넣고 그 안에서만 사용할 수 있도록 접근 범위를 최소로 줄이기

가변 데이터(Mutable Data)

함수형 프로그래밍에서는 데이터는 절대 변하지 않고, 변경하려면 반드시 변경하려는 값에 해당하는 복사본을 만들어서 반환한다는 개념이 기본.

무분별한 데이터 수정에 따른 위험을 줄이는 방법

  • 변수 캡슐화하기 -> 정해놓은 함수를 거쳐야만 값을 수정할 수 있도록 하면 값이 어떻게 수정되는지 감시하거나 코드를 개선하기 쉬움

  • 변수 쪼개기 -> 용도별로 독립 변수에 저장하게 하여 값 갱신이 문제를 일으킬 여지를 없애기

  • 갱신 로직은 다른 코드와 떨어뜨려 놓는 것이 좋으며, 문장 슬라이드하기와 함수 추출하기를 이용해서 무언가를 갱신하는 코드로부터 부작용이 없는 코드를 분리

  • API 만들 때 질의 함수와 변경 함수 분리하기를 활용해 필요한 경우가 아니라면 부작용이 있는 코드를 호출할 수 없도록 만들기

  • Setter 제거

  • 파생 변수를 질의 함수로 바꾸기 기법 사용(코드 전체에 골고루 뿌리기)

  • 변수의 유효범위가 단 몇줄이면 가변 데이터라해도 문제를 일으킹 가능성이 적겠지만, 나중에 유효범위가 넓어질 수 있고, 위험이 커짐

  • 여러 함수를 클래스로 묶기, 여러 함수를 변환 함수로 묶기 를 활용해서 변수를 갱신하는 코드들의 유효범위를 제한함

  • 구조체처럼 내부 필드에 데이터를 담고 있는 변수라면, 일반적으로 참조를 값으로 바꾸기를 적용하여, 내부 필드를 직접 수정하지말고 구조체를 통째로 교체하는 편이 좋음

뒤엉킨 변경(Divergent Change)

뒤엉킨 변경은 단일 책임 원칙(Single Responsibility Principle, SRP) 이 제대로 지켜지지 않을 때 나타남. (하나의 모듈이 서로 다른 이유들로 인해 여러 가지 방식으로 변경되는 경우)

ex) 지원해야할 DB가 추가될 때마다 함수 세 개를 바꿔야 하고, 금융 상품이 추가될 때마다 또 다른 함수 네 개를 바꿔야 하는 모듈이 있다면 뒤엉킨 변경.

예시의 경우 DB 연동과 금융 상품 처리는 서로 다른 맥락에서 이루어지므로 독립된 모듈로 분리해야 프로그래밍이 편함

개발 초기에는 맥락 사이의 경계를 명확하게 나누기가 어렵고 소프트웨어 시스템의 기능이 변경되면서 경계도 끊임없이 움직임

단계쪼개기 -> 순차적으로 실행되는 게 자연스러운 맥락이라면, 다음 맥락에 필요한 데이터를 특정한 데이터 구조에 담아 전달하게 하는 식으로 단계 분리

함수 옮기기 -> 전체 처리 과정 곳곳에서 각기 다른 맥락의 함수를 호출하는 빈도가 높다면, 각 맥락에 해당하는 적당한 모듈들을 만들어서 관련 함수들을 모음

함수 추출한다면 여러 맥락의 일에 관여하는 함수가 있다면 옮기기 전에 수행하기

산탄총 수술(Shotgun Surgery)

뒤엉킨 변경과 비슷하면서 정반대

함수 옮기기, 필드 옮기기-> 변경할 부분이 코드 전반에 퍼져있다면 찾기도 어렵고 수정해야 할 곳을 지나치기 쉬움

비슷한 데이터를 다루는 함수가 많다면, 여러 함수를 클래스로 묶기!

데이터 구조를 변환하거나 보강(enrich)하는 함수들 -> 여러 함수를 변환 함수로 묶기

이렇게 묶은 함수들의 출력 결과를 묶어서 다음 단계의 로직으로 전달(단계 쪼개기)

메서드나 클래스가 비대해지지만 나중에 추출하기 리팩터링으로 더 좋은 형태로 분리 가능

기능 편애(Feature Envy)

함수를 데이터 근처로 옮겨주기(함수 옮기기)

함수의 일부에서만 기능 편애

→ 독립 함수로 빼내기(함수 추출하기)
→ 원하는 모듈로 보내주기(함수 옮기기)

함수가 사용하는 모듈이 다양한 경우에는

  • 가장 많은 데이터를 포함한 모듈로 옮긴다.
  • 함수 추출하기로 함수를 여러 조각으로 나눈 후 각각을 적합한 모듈로 옮기면 더 쉽게 해결
  • 위에서 설명한 규칙을 거스르는 복잡한 패턴들도 존재함

전략 패턴(Strategy Pattern), 방문자 패턴(Visitor Pattern), 켄트백의 자기 위임(Self-Delegation)
뒤엉킨 변경 냄새를 없앨 때 활용하는 패턴들
‘함께 변경할 대상을 한데 모으는 것’
오버라이드해야 할 소량의 동작 코드를 각각의 클래스로 격리해주므로 수정하기가 쉬워짐(대신 간접호출은 늘어남)

데이터 뭉치(Data Clumps)

데이터 항목들은 서너 개가 여러 곳에서 항상 함께 뭉쳐다니는 모습을 볼 수 있음. 이런 데이터 뭉치는 보금자리를 따로 마련해줘야함

여기서 데이터뭉치는 ‘여러 개의 값이 하나의 논리적인 개념으로 묶여 있는 경우’를 의미

처리한다는 것은 데이터 뭉치를 찾아서 ‘클래스 추출하기로 하나의 객체를 묶는 것’

값 하나를 삭제해 보았을 때 의미가 깨지거나 일관성이 사라질 경우. 데이터 뭉치임.

클래스로 만들면 좋은 향기를 흩뿌릴 기회가 생기며, 그 클래스로 옮기면 좋을 동작은 없는지 살펴보자.

기본형 집착(Primitive Obsession)

대부분의 프로그래밍 언어는 정수, 부동소수점 수, 문자열 같은 다양한 기본형(primitive type)을 제공

프로그래머 중에는 자신에게 주어진 문제를 딱 맞는 기초 타입(화폐, 좌표, 구간 등)을 직접 정의하기를 몹시 꺼리는 사람이 많음

전화번호를 단순히 문자 집합으로만 표현
최소한 사용자에게 보여줄 때는 일관된 형식으로 출력해주는 기능이라도 갖춰야함
이런 문자열 악취는 아주 흔해서, ‘문자열화된(stringly typed) 변수’라는 이름까지 붙음
기본형을 객체로 바꾸기
프로그래머는
타입 코드를 서브클래스로 바꾸기
조건부 로직을 다형성으로 바꾸기
데이터 뭉치가 보일 경우(자주 함께 몰려다니는 기본형 그룹도 데이터 뭉치임)
클래스 추출하기, 매개변수 객체 만들기

반복되는 switch 문(Repeated Switches)

객체 지향을 신봉하는 사람들과 얘기하다 보면 곧 switch문의 사악함으로 흘러감

이들은 코드에 등장하는 switch문은 모조리 조건부 로직을 다형성으로 바꾸기로 없애야 할 대상으로 주장함. 심지어 모든 조건부 로직을 다형성으로 바꿔서 if문까지도 대부분 휴지통에 쓸어 담아야한다고 주장하는 이도 있음.

1990년대 후반까지만 하더라도 다형성의 가치를 제대로 아는 사람이 적었고, switch문 냄새가 사람들이 다형성을 이용하도록 전환하는데 도움이 되었기 때문

지금은 널리 자리잡아서 switch문을 썼다고 자동으로 검토 대상은 되지는 않는 세상이 됨. 분기 조건에 몇가지 기본형만 쓸 수 있던 예전과 달리, 문자열 등의 복잡한 타입까지 지원하는 발전된 switch문을 제공하는 언어도 많아짐. 조건부 로직(switch/case문, 길게 나열된 if/else문)이 여러 곳에서 반복해 등장하는 코드에 집중해보자.

중복된 switch문이 문제가 되는 이유는 조건절을 하나 추가할 때마다 다른 switch문들도 모두 찾아서 함께 수정해야 하는게 문제

조건부 로직(switch/case 문이나 길게 나열된 if/else문)이 여러 곳에서 반복해 등장하는 코드에 집중해보기
중복된 switch문이 문제가 되는 이유는 조건절을 하나 추가할 때마다 다른 switch 문들도 모두 찾아서 함께 수정해야 하기 때문. 이럴 때 다형성은 반복된 switch문이 내뿜는 사악한 기운을 제압하여 코드베이스를 최신 스타일로 바꿔주는 세련된 무기.

반복문(Loops)

일급 함수(first-class function)을 지원하는 언어가 많아져서 솜털 무늬 벽지보다도 못한 존재가 되어버림

반복문을 파이프라인으로 바꾸기를 적용해서 시대에 걸맞지 않은 반복문을 제거할 수 있음.

필터(filter)나 맵(map)같은 파이프라인 연산을 사용하면 코드에서 각 원소들이 어떻게 처리되는지 쉽게 파악할 수 있음.

성의 없는 요소(Lazy Element)

코드의 구조를 잡을 때 프로그램 요소를 이용하는 걸 좋아함

하지만 그 구조가 필요 없을 때도 있음. 본문 코드를 쓰는 것과 진배없는 함수도 있고, 실질적으로 메서드가 하나뿐인 클래스도 있음.

나중에 본문을 더 채우거나 다른 메서드를 추가할 생각이었지만, 어떠한 사정으로 인해 그렇게 하지 못한 결과일 수도 있음.

원래는 풍성했던 클래스이지만 리팩터링을 거치면서 역할이 줄어들었을 수 있음.

이런건 고이 보내드리자

함수 인라인하기, 클래스 인라인하기 로 처리
상속을 사용했다면 계층 합치기 적용

추측성 일반화”(Speculative Generality)

‘추측성 일반화’_ 브라이언 푸트(Brian Foote)

‘나중에 필요할 거야’ 라는 생각으로 당장은 필요 없는 모든 종류의 후킹(hooking)포인트와 특이 케이스 처리 로직을 작성해둔 코드에서 풍김 그 결과는 물론 이해하거나 관리하기 어려워진 코드임. 사용하지 않는다면 쓸데없는 낭비임.걸리적거리는 코드는 지워버리자

계층 합치기 적용 -
하는 일이 거의 없는 추상클래스는 제거. 쓸데없이 위임하는 코드는 함수 인라인하기나 클래스 인라인하기로 삭제
본문에서 사용되지 않는 매개변수는 함수 선언 바꾸기로 없앰
추측성 일반화는 테스트 코드 말고는 사용하는 곳이 없는 함수나 클래스에서 흔히 볼 수 있음. 이런 코드를 발견하면 테스트 케이스부터 삭제한 뒤에 죽은 코드 제거하기로 날려버리기.

임시 필드(Temporary Field)

간혹 특정 상황에서만 값이 설정됟는 필드를 가진 클래스도 있음. 하지만 객체를 가져올 때는 당연히 모든 필드가 채워져 있으리라 기대하는게 보통. 이렇게 임시 필드를 갖도록 작성하면 코드를 이해하기 어려움. 쓰이지 않는 것처럼 보이는 필드가 존재하는 이유를 파악하느라 고민해야함
클래스 추출하기(덩그러니 떨어져있는 필드 모으기)

함수 옮기기(임시 필드와 관련된 코드를 모조리 새 클래스에 몰아 넣기)

특이 케이스 추가

(임시 필드들이 유효한지를 확인한 후 동작하는 조건부 로직이 있을 수 있음.필드들이 유효하지 않을 때를 위한 대안 클래스를 만들어서 제거할 수 있음)

메시지 체인(Message Chains)

메서드 체인은 클라이언트가 한 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에 또다른 객체를 요청하는 방식으로, 다른 객체를 요청하는 작업이 연쇄적으로 이어지는 코드

getSomething() 처럼 getter가 꼬리에 꼬리를 물고 이어지거나 임시 변수들이 줄줄이 나열되는 코드들이 있음. 이는 클라이언트가 내비게이션 구조에 종속됐음을 의미함. 그래서 내비게이션 중간 단계를 수정하면 클라이언트 코드도 수정해야함.

이 문제는 위임숨기기로 해결함. 메시지 체인의 다양한 연결점에 적용할 수 있음. 모든 객체에 적용할 수 있지만 그러다보면 중간 객체들이 모두 중개자다 돼버리기 쉬움. 그러니 최종 결과 객체가 어떻게 쓰이는지부터 살펴보는게 좋음.

함수 추출하기(결과 객체를 사용하는 코드 일부를 따로 빼낸 다음)

함수 옮기기(체인을 숨길 수 있는지 확인해보기)

체인을 구성하는 객체 중 특정 하나를 사용하는 클라이언트 중 그 이후의 객체들도 사용하길 원하는 클라이언트가 제법 된다면, 이 요구를 처리해줄 메서드를 추가함.

마지막으로 체인의 중간인 부서 정보를 얻어 사용하는 다수의 클라이언트가 부서장 이름도 함께 사용한다면 부서 클래스에 managerName() 메서드를 추가하여 체인을 단축할 수 있음

중개자(Middle Man)

캡슐화(encapsulation)는 객체의 대표적인 기능 중 하나. 외부로부터 세부사항을 숨겨줌.

캡슐화과정에서는 위임(delegation)이 자주 활용됨.

ex) 팀장에게 미팅을 요청하는 상황. 팀장은 자신의 일정을 확인한 후 답을 줌. 팀장이 종이 다이어리를 쓰든, 일정 서비스를 쓰든, 비서를 두든 알 바 아님

하지만 지나치면 문제. 클래스가 제공하는 메서드 중 절반이 다른 클래스에 구현을 위임하고 있다면?

이럴때는 중재자 제거 필요(실제로 일을 하는 객체와 직접 소통하게 만들기)**를 활용. 위임 메서드를 제거한 후 남는 일이 거의 없다면 호출하는 쪽으로 인라인하기(함수 인라인하기 사용)

내부자 거래(Insider Trading)

소프트웨어 개발자는 모듈 사이에 벽을 두껍게 세우기를 좋아하며, 그래서 모듈 사이의 데이터 거래가 많으면 결합도(coupling)가 높아진다고 투덜댐. 일이 돌아가게 하려면 거래가 이뤄질 수 밖에 없지만, 그 양을 최소로 줄이고 모두 투명하게 처리해야함.

함수 옮기기, 필드 옮기기 (떼어놓아서 사적으로 처리하는 부분 줄이기)

위임 숨기기 (여러 모듈이 같은 관심사를 공유한다면 공통 부분을 정식으로 처리하는 제3의 모듈을 새로 만들기 → 다른 모듈이 중간자 역할을 하게 만들기)

[상속 구조] 자식 클래스는 항상 부모 클래스가 공개하고 싶은 것 이상으로 부모에 대해 알려고함. 그러다가 부모 곁을 떠나야 할 때가 온다면 서브클래스를 위임으로 바꾸기 or 슈퍼클래스를 위임으로 바꾸기 를 활용하자.

거대한 클래스(Large Class)

한 클래스가 너무 많은 일을 하려다 보면 필드 수가 상당히 늘어남. 클래스에 필드가 너무 많으면 중복이 발생하기 쉬움 →

클래스 추출하기로 필드들 일부를 따로 묶기(같은 컴포넌트에 모아두는 것이 합당해 보이는 필드들을 선택) 가령 depositAmount와 depositCurrency 필드는 같은 컴포넌트에 두는 것이 좋을 것.

분리할 컴포넌트를 원래 클래스와 상속 관계로 만드는게 좋다면 (클래스 추출하기보다) 슈퍼클래스 호출하기나 (실질적으로 서브클래스 추출하기에 해당하는) 타입 코드를 서브클래스로 바꾸기를 적용하는 편이 더 쉬울 것

클래스가 항시 모든 필드를 사용하지는 않을 수도 있음. 이럴 때는 앞에서 언급한 추출 기법들을 여러 차례 수행해야 할지도 모름

필드가 너무 많은 클래스와 마찬가지로 코드량이 너무 많은 클래스도 중복 코드와 혼동을 일으킬 여지가 큼. 가장 간단한 해법은 그 클래스 안에서 자체적으로 중복을 제거하는 것(간단한 해결책). 가령 부분부분 상당량의 로직이 똑같은 100줄짜리 메서드 다섯개가 있다면 각각의 공통 부분을 작은 메서드들로 뽑아내자.

유용한 기능 그룹을 찾았다면 클래스 추출하기, 슈퍼클래스 추출하기, 타입 코드를 서브클래스로 바꾸기 등을 활용해서 여러 클래스로 분리함

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

클래스를 사용할 때의 큰 장점은 필요에 따라 언제든 다른 클래스로 교체할 수 있다는 점. 교체하려면 인터페이스가 같아야함.

함수 선언 바꾸기로 메서드 시그니처를 일치시킴.
부족하면 함수 옮기기를 이용하여 인터페이스가 같아질 때까지 필요한 동작들을 클래스 안으로 밀어 넣음.
그러다 대안 클래스들 사이에 중복 코드가 생기면 슈퍼클래스 호출하기를 적용할지 고려해봄

데이터 클래스(Data Class)

데이터 필드와 getter/setter로만 구성된 클래스를 말함. 데이터 저장 용도로만 쓰이다 보니 다른 클래스가 너무 깊이까지 함부로 다룰 때가 많음. 이런 클래스에 public 필드가 있다면 누가 보기 전에 얼른 레코드 캡슐화하기로 숨기자. 변경하면 안되는 필드는 세터 제거하기로 접근을 원천 봉쇄함.

다른 클래스의 데이터 클래스의 getter나 setter를 사용하는 메서드를 찾아서 함수 옮기기로 그 메서드를 데이터 클래스로 옮길 수 있는지 살펴보기.

메서드를 통채로 옮기기가 힘들다면 함수 추출하기를 이용하여 옮길 수 있는 부분만 별도 메서드로 뽑아내기

데이터 클래스는 필요한 동작이 엉뚱한 곳에 정의돼 있다는 신호일 수 있음. 이런 경우라면 클라이언트 코드를 데이터 클래스로 옮기기만 해도 대폭 개선됨.

특히 다른 함수를 호출해 얻은 결과 레코드(데이터 객체)로는 동작 코드를 넣을 이유가 없음

단계 쪼개기의 결과로 나온 중간 데이터 구조가 있음. 이런 데이터 구조는 (적어도 현실에서 활용되는 모습상으로는) 불변(immutable)임. 불변 필드는 굳이 캡슐화할 필요가 없고, 불변 데이터로부터 나오는 정보는 getter를 통하지 않고 그냥 필드 자체를 공개해도 됨

상속 포기(Refused Bequest)

서브클래스는 부모로부터 메서드와 데이터를 물려받음. 하지만 부모의 유산을 원치 않거나 필요 없다면 어떻게 해야 할까? 수많은 유산중 일부만 받기. 예전엔 계층구조를 잘못 설계했기 때문으로 봤음.

메서드 내리기와 필드 내리기를 활용
이 관점에서의 해법은, 먼저 같은 계층에 서브클래스를 하나 새로 만듦
물려받지 않을 부모 코드를 모조리 새로 만든 서브클래스로 넘김.
부모 클래스는 모두 추상 클래스여야 한다고 말하는 사람도 많음.

일부 동작을 재활용하기 위한 목적으로 상속을 활용하기도 하는데, 실무 관점에서 아주 유용한 방식임.

상속을 포기할 시 혼란과 문제가 생긴다면 앞에서 설명한 예전 방식을 따름

무조건 이래야 한다는 생각은 버리기. 열에 아홉은 냄새가 미미해서 씻어내지 않아도 됨.

상속 포기 냄새는 서브 클래스가 부모의 동작은 필요로하지만 인터페이스는 따르고 싶지 않을때 특히 심하게 남. 구현을 따르지 않는건 이해가 됨. 하지만 인터페이스를 따르지 않는 것은 상당히 무례한 태도임.

이럴 때는 서브클래스를 위임으로 바꾸기나 슈퍼클래스를 위임으로 바꾸기를 활용해서 아예 상속 매커니즘에서 벗어나자.

주석(Comments)

주석이 장황하게 달린 원인이 코드를 잘못 작성했기 때문인 경우가 있음

함수 추출하기 -> 특정 코드 블럭이 하는 일에 주석을 남기고 싶으면 사용
함수 선언 바꾸기 -> 추출되어 있는 함수임에도 여전히 설명이 필요하다면 해당 기법 적용하여 이름 수정
Assertion 추가하기 -> 시스템이 동작하기 위한 선행조건을 명시하는 경우
주석을 남겨야겠다는 생각이 든다면 -> 가장 먼저 주석이 필요없는 코드로 리팩터링

현재 진행 상황, 확실하지 않은 부분, 코드를 지금처럼 작성한 이유를 설명하는 용도, 나중에 코드를 수정해야 할 프로그래머에게 도움이 될 것!

출처