리팩토링 독서 노트

ChoiYongHyeun·2024년 1월 12일
0

프로그래밍 공부

목록 보기
1/17

마틴파울러의 리팩토링을 읽으며 배운 내용들을 러프하게 작성합니다 ..

열심히 필기하기에는 너무 필기하는게 많아서 해당 내용은 그냥 정말 필기 수준으로만..


01/12/2024

현재는 개요 부분이라 키워드로 정리되어있고 , 추후 키워드들에 대한 내용이 다뤄질 예정이다.
챕터 1,2 는 추후 나올 내용들을 실습을 먼저 했었는데 상당히 간결 구웃

- Chapter 3 : 코드에서 나는 악취

내 코드에서 주로 나는 냄새

- 중복 코드

동일한 메소드가 중복된다면 함수 추출하기 사용하기

같은 부모로부터 파생된 서브 클래스들에 코드가 중복되어있다면 부모 클래스로 메소드 올리기

- 긴 함수

예전엔 함수 호출 비용이 높았기 때문에 한 함수가 길게 유지되는걸 추천했지만 요즘엔 함수 호출 비용이 매우 적기 때문에 함수를 짧게 구성하는것이 좋다.

99%는 함수 추출하기가 차지한다.

함수가 매개변수와 임시 변수를 많이 사용한다면 임시 변수를 질의 함수로 바꾸기

임시변수의 수를 매개변수 객체 만들기 , 객체 통째로 넘기기 통해 매개변수의 수를 줄일 수 있다.

여전히 너무 많다면 큰 수술이라 할 수 있는 함수를 명령으로 바꾸기 를 고려하자

조건문은 조건문 분해하기 를 사용하거나 case switch 문을 이용해 조건부 로직을 다형석으로 바꾸기 를 사용하자

반복문 코드를 함수화 시킬 적절한 이름이 떠오르지 않는다면 두 가지 작업이 섞여있어서 그럴 수 있으니 과감하게 반복문 쪼개기 를 적용해라

- 긴 매개변수 목록

긴 매개변수 목록은 위에서 말했듯 매개변수를 질의 함수로 바꾸기 를 사용하자

혹은 객체를 통째로 넘기기매개변수 객체 만들기 , 플래그 인수 제거하기 를 사용하자

또 여러 함수가 특정 매개변수들의 값으로 공통으로 사용되면 여러 함수를 클래스로 묶기 를 사용하자

- 전역 데이터

드디어~!!!! 내가 너무 고민하던 전역 데이터 ..

다양한 함수들이 참조하는 전역 데이터는 코드베이스 어디에서든 건드릴 수 있고 값을 누가 바꿨는지 찾아낼 매커니즘이 없기 때문에 최대한 지양해야 한다.

방지하기 위핸 대표적인 리팩토링은

변수 캡슐화하기 이다.

데이터를 함수화 시켜 감싸 접근을 통제하거나 클래스나 모듈에 집어넣고 그 안에서만 사용 할 수 있도록 접근 범위를 최소화 하자

- 가변 데이터

가변 데이터를 다루는건 더 골치가 아프다.

변수 캡슐화 하기 를 적용해 정해놓은 함수를 거쳐야만 값을 수정 할 수 있게 만들어 값이 수정되는지를 감시하자

용도 별로 변수 쪼개기 를 사용하거나

갱신이 필요한 변수는 갱신이 필요한 로직을 추출하여 함수로 만들기 하자

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

가능한 세터 제거하기 를 적용한다.

갸아악 전역 변수를 함수 내에 집어넣고 getter / setter 를 사용해야하나 ? 이랬는데 하지말란다

변수의 유효 범위가 넓을 수록 생길 수 있는 위험이 많으니 여러 함수를 클래스로 묶거나 여러 함수를 변환 함수로 묶자

객체 내의 데이터를 변경 할 것이라면 참조하고 있는 원 객체의 값을 수정하기 보다 값으로 바꿔 관리 후 객체를 바뀐 객체로 통째로 바꾸자

- 뒤엉킨 변경

코드를 수정 할 떄는 시스템에서 고쳐야 할 딱 한군데를 찾아서 그 부분만을 수정해야 한다.

그런데 여기 고치면 저기 고치고 이래야 하면 이는 코드에서 악취가 나고 있기 때문이다

이는 단일 책임 원칙 (Single Responsbility Principle) 이 제대로 지켜지지 않았기 때문이다.

순차적으로 실행되는게 자연스런 맥락이라면

데이터 구조가 변경 될 때 마다의 단계를 나누고 단계 쪼개기

단계 별 객체를 반환하는 함수를 만들고 함수 추출하기

해당 함수들을 적당한 모듈에 담자 함수 옮기기, 클래스 추출하기

- 산탄총 수술

산탄총 수술은 뒤엉킨 변경과 비슷하면서 정 반대다

코드를 변경 할 때 마다 자잘하게 수정해야 하는 클래스가 많을 때 풍긴다.

산탄총 수술 vs 뒤엉킨 변경

구분 원인 발생과정 해법 (원리) 해법 (실제 행동)
산탄총 수술 여러 부분에서 동일한 기능을 수행하는 코드의 분산 특정 부분을 수정하면 다른 여러 부분에서 예상치 못한 오류 발생 모듈화 및 코드의 응집도 향상 코드를 모듈화하고 관련된 부분을 함께 수정
뒤엉킨 변경 종속성이 높은 코드 변경 한 부분을 수정할 때 해당 부분과 밀접한 관련된 다른 부분도 수정해야 함 종속성을 최소화하고 독립적인 모듈화 한 모듈을 수정할 때 해당 모듈과 직접적으로 관련된 부분만 수정

어떤 함수가 다른 함수들에 영향을 미치는, 마치 산탄총이 퍼져나가는 듯한 내용을 이야기 한다.

- 기능 편애

편애란 표현보다 영어 원문 그대로인 Feature Envy , 질투가 더 비슷한 느낌

모듈화 할 때 코드는 여러 영역으로 나눠 (같은 기능을 하는 코드들을 한 영역에 담기) 동일한 영역 내 함수들은 상호작용을 최대화 하고

서로 다른 영역간의 상호작용은 최소화 해야한다.

모듈들의 명확한 영역 나누기

근데 어떤 영역 내에 존재하는 함수가 다른 영역의 함수와 더 이야기를 많이 나눈다면 ..

얘는 소원대로 상호작용을 더 많이하는 영역 근처로 옮겨주면 된다.

혹은 함수의 일부에서만 기능을 사용한다면, 해당 함수의 일부 기능을 독립 함수로 빼낸 다음 (함수 추출하기) 원하는 모듈로 보내준다. 함수 옮기기

디자인 패턴 중 전략 패턴과 방문자 패턴이 있으며 자기 위임도 여기 속한다고 한다. 디자인 패턴도 공부해야지 ..

데이터 뭉치 (Data Clumps)

같이 자주 사용되는 데이터들이 존재한다면 뭉쳐버리자

데이터 뭉치를 찾아서 클래스 추출하기 로 하나의 객체로 묶자

그 후 클래스 내부에 존재하는 데이터들을 객체로 만들어 하나로 또 묶자

그렇게 해서 해당 데이터들을 한 뭉치로 묶어

다른 메소드에서 해당 데이터들을 부를 때 따로따로 부르지말고 객체 자체를 부르도록 하자 매개변수 객체 만들기 , 객체 통째로 넘기기

데이터 뭉치가 필요한지 확인하는법 => 뭉쳐다니는 데이터 중 하나의 데이터라도 없다면 의미가 없다면 걔넨 영혼의 단짝이야 그니까 묶어버려

고민

어떤 사람들은 상속을 최대한 줄이기 위해 클래스를 사용하지 말라는 사람도 있다. 고민해보자 다 읽어보고 다른 책도 읽어봐야지

기본형 집착

프로그래밍 언어는 주로 정수, 부동소수점 수, 문자열 등 기본형 을 제공한다.

혹은 날짜등을 간단한 객체로 제공한다.

프로그래머는 사용 중 자신에게 주어진 문제에 딱 맞는 기초 타입을 정의하기 꺼려한다.

예를 들어 달러 단위의 화폐 , 좌표 , 구간 등 ... 그냥 기본형으로 해버리는 경우가 많음

정신차려

반복되는 Switch

순수한 객체 지향을 신봉하는 사람들과 얘기하다보면 switch 문의 사악함으로 이야기가 흘러간다.

더 과격한 사람은 if 문도 없애버려야 한다고 한다.

이 사람들은 조건에 따른 로직을 만들 빠엔 전부 각기 다른 객체로 만들어 버리고 각기 다른 객체들의 독립적인 메소드로 행동을 제어하길 원한다.

그럼 리팩토링 입장에서 조건에 따른 로직을 지양해야 하는 이유는 다음과 같다.

어떤 조건이 추가 될 때 마다 코드 내부에 존재하는 해당 조건에 따른 로직을 모 ~ 두 고쳐야 한다.

그렇게 할빠에는 조건부 로직을 다형성 으로 바꾸자

위에서 말한 것처럼 객체로 만들어버려서 객체별로 행동을 하도록 , 어떤 조건이 추가 된다면 해당 조건을 가진 객체를 만들어버려~!!

이러한 방식은 코드를 확장 가능하고 유지보수가 쉬운 형태로 만들어주며, 객체 지향 프로그래밍의 핵심 개념 중 하나인 "개방 폐쇄 원칙(Open-Closed Principle)"을 따르게 한다

개방 폐쇄 원칙

반복문

반복문은 코드의 가독성을 해친다.

다행히 자바스크립트는 다양한 일급 함수를 지원한다.

반복문을 파이프라인으로 바꾸기 를 통해서 시대에 걸맞지 않은 반복문을 제거하자

성의 없는 요소

함수나 클래스를 단순하게 만들고 , 수정을 거치며 가볍게 덜다보니 너무나도 가볍게 되는 경우가 있다.

뭐 걍 본문에서 코드로 쓰는 것과 다름 없는 메소드가 있거나 추후 클래스에 메소드를 더 추가하려 했으나 사정상 메소드를 추가하지 않은 단순한 클래스 (메소드가 1개밖에 존재하지 않는) 가 존재할 수 있다.

이러면 걍 고이 보내버려

함수 인라인하거나 클래스 인라인 하기로 처리한다.

상속을 사용했다면 계층 합치기를 적용한다.

임시 필드

특정 상황에서만 값이 설정되는 필드를 가진 클래스도 존재한다.

사용자는 어떤 객체를 가져 올 떄 당연히 해당 객체에 값이 채워져있을거라 기대하는게 보통이다.

이런 슈뢰딩거의 고양이 마냥 값이 존재 할 수도 없을 수도 있는 객체가 존재하면 (임시필드) 사용자는 골치가 아프다.

이런 필드를 만나면 클래스 추출하기 로 제 살곳을 찾아주고 , 함수 옮기기 로 임시 필드들과 관련된 코드를 모조리 새 클래스에 몰아주자

또한 임시 필드들이 유효한지 확인 한 후 동작하는 조건부 로직이 존재한다면 (임시 필드에 값이 존재한다면 .. 뭐 어쩌구 하기) 특이 케이스 추가 하기 로 필드들이 유효하지 않을 때를 위한 대안 클래스를 만들어 해당 필드를 대안 클래스 내부로 넣어주자

메시지 체인

메시지 체인은 객체를 통해 다른 객체를 얻은 뒤 방금 얻은 객체에서 또 다른 객체를 요청하는 .. 연쇄적으로 이어지는 코드를 의미한다.

수 많은 게터들이 꼬리에 꼬리를 무는 ..

이는 해당 메시지 체인에서 한 부분만 변경하더라도 이후 체인을 모두 수정해야 한다.

이 문제는 위임 숨기기 로 해결한다.

중개자 (Middle Man)

객체의 대표적인 기능 하나로, 외부로부터 세부사항을 숨겨주는 캡슐화가 있다.

캡슐화 과정에선 위임이 자주 활용된다.

여기까지 읽다가 내가 객체지향 프로그래밍을 제대로 이해하고 있는게 맞나 ? 싶어서 객체지향 근본서적이라 불리는 토끼책을 구매했다.
18000원 개꿀

예를 들어 팀장한테 미팅을 요청한다고 해보자

팀장은 자신의 일정을 확인한 후 답을 주는데 뭐 팀장이 일정을 어디에 쓰든 우리 알바가 아니다

클래스가 제공하는 메소드 중 절반이 다른 클래스 구현을 위해 위임하고 있다면 그 메소드는 해당 클래스에 존재해야 할까 ? 그냥 중개만 하는데 ?

스튜핏

중개자 제거하기 를 활용하자

실제 일을 하는 객체와 직접 소통하도록 하자

내부자 거래 (Insider Trading)

소프트 웨어 개발자는 모듈 사이에 벽을 두껍게 세우기를 좋아한다.

모듈 사이의 데이터 거래가 많으면 결합도 (Coupling)가 높아진다고 투덜댄다.

은밀하게 데이터를 주고 받는 모듈들이 있다면 함수 옮기기 , 필드 옮기기 기법으로 떼어 놓아서 사적으로 처리하는 부분을 줄이자

같은 관심사를 가지는 모듈이 존재한다면 공통 부분을 정식으로 처리하는 제3의 모듈을 새로 만들거나 위임 숨기기를 이용하여 다른 모듈이 중간자 역할을 하게 만들자

상속 구조에서 자식 클래스가 부모 클래스가 제공하는 정보 이상으로 부모 클래스에 대해 알고싶어하거나 부모 폼을 떠나야 한다면 떠나 보내주자

서브 클래스를 위임으로 바꾸기 , 슈퍼클래스를 위임으로 바꾸기 를 활용하자

거대한 클래스

한 클래스가 너무 많은 일을 하려다 보면 필드 수가 상당히 늘어난다.

그리고 클래스에 필드가 너무 많으면 중복 코드가 생기기 쉽다

ㅇㅈ

이럴 떄 클래스 추출하기 로 필드들 일부를 따로 묶는다.

필드가 너무 많은 클래스와 마찬가지로 코드량이 너무 많은 클래스도 중복 코드와 혼동을 일으킬 여지가 너무 크다.

가장 간단한 해법은 클래스 안에서 자체적으로 중복을 제거하는 것이다.

가령 상당 부분 로직이 똑같은 100줄짜리 메소드 다섯개가 있다면, 공통 부분을 작은 메소드들로 뽑아내고 원래 다섯 메소드들에는 작은 메소드들을 호출하는 코드 10줄 가량만 남게 될 것이다.

공통부분 추출하고 거기서 호출해서 써 ~!~!~~~!!@!!@!@!@@!@!!@

데이터 클래스

데이터 클래스는 데이터 플디와 게터 / 세터 메소드로만 구성된 클래스를 말한다.

그저 데이터 저장 용도로만 쓰이다 보니 다른 클래스가 너무 깊이까지 함부로 다룰 때가 많다.

최대한 전역 변수들의 사용을 지양해야 한다고 했다. 엄격한 함수형 프로그래밍에선 데이터 자체를 불변하게 쓰는 것을 지향한다고 한다.

변경하면 안되는 필드는 세터 제거하기 로 불변하게 만들자

다른 변수에서 참조하면 안되는 것은 레코드 캡슐화하기 를 통해 참조 못하게 가려버리자

불변 필드는 굳이 캡슐화 할 필요가 없다. 불변 데이터로부터 나오는 정보는 게터를 통하지 않고 그냥 필드 자체를 공개해도 된다.

다만 필드를 공개한다는 것은 전역에다가 발라당 내놓으란게 아니다 . 레코드 캡슐화를 통해 접근을 어렵게 할 수 있다.

ex : 함수 안에 넣어서 달라고 요청을 보내면 return 하기만 하기

상속 포기

서브 클래스는 부모로부터 매소드와 데이터를 물려 받는다.

하지만 부모의 유산을 원하지 않으면 어캐

수 많은 유산 중 몇 개의 유산만 관심 있는 서브 클래스가 존재 할 수도 있다.

이는 계층 구조를 잘못 설계해서 그렇다.

해법은 같은 계층에 서브 클래스를 하나 만들고 메소드 내리기와 필드 내리기를 활용해 물려받지 않을 부모 코드를 모조리 새로 만든 서브클래스로 넘긴다.

그렇게 해서 부모 클래스에는 자식 클래스에게 넘길 공통적인 메소드만 남기자

만약 자식 클래스가 부모 클래스의 동작은 필요로 하지만 인터페이스는 따르고 싶지 않는다면 서브 클래스를 위임으로 바꾸기슈퍼클래스를 위임으로 바꾸기 를 활용하자

주석

주석을 달면 안된다고 하는건 아니다 .

하지만 주석이 장황하게 달려야 하는 코드는 악취가 나는 코드다

웬만해서 주석을 남겨야 하는 생각이 들면 가장 먼저 주석이 필요없는 코드로 리팩토링 하자

함수 추출하기로 행위들을 쪼개본다거나

함수의 명으로 어떤 일을 하는지 나타낼 수 있도록 함수 선언을 바꿔본다거나

선행 조건을 명시하고 싶으면 어서션 추가하기 를 하자

profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글