diff --git "a/jin/[Refactoring] \354\275\224\353\223\234\354\227\220\354\204\234 \355\222\215\352\270\260\353\212\224 \354\225\205\354\267\250.md" "b/jin/[Refactoring] \354\275\224\353\223\234\354\227\220\354\204\234 \355\222\215\352\270\260\353\212\224 \354\225\205\354\267\250.md" new file mode 100644 index 0000000..61c3098 --- /dev/null +++ "b/jin/[Refactoring] \354\275\224\353\223\234\354\227\220\354\204\234 \355\222\215\352\270\260\353\212\224 \354\225\205\354\267\250.md" @@ -0,0 +1,615 @@ +# 코드에서 나는 악취 - 1 +## 읽기 전에.. +1. 이 글은 개인 생각과 경험이 많이 포함된 글이니 걸러서 읽어 주셔야 합니다.. +2. 중복을 고려하지 않습니다. 중요한 내용이라면 또 말하고 또 말하는 부분이 많습니다. 제가 편하게 부분 부분 가져다가 다른 곳에 쓰기 위함입니다. + + +# 0. 리팩터링은 언제 해야 하는가? +리팩터링은 "언제" 해야 할까?
+리팩터링의 원리를 아는 것 만큼, 리팩터링을 언제 시작하고, 언제 그만할지 판단하는 일도 매우 중요하다. 대부분 모호한 기준을 가질 수 밖에 없다.
+켄트 벡과 마틴 파울러는 결국 어느 정도 직관에 기댈 수 밖에 없다고 이야기한다. 다만 "징후"는 정리해볼 수 있다. 메서드가 몇 줄을 넘어가면 읽기가 어렵더라, Depth가 몇 칸을 넘어가면 읽기가 힘들더라 등의 "징후"는 정리해볼 수가 있다.
+ +켄트 백은 이런 코드들에 "악취"가 난다고 표현했다. 갓 태어난 딸을 돌보느라 기저귀 냄새에 예민해져 이런 표현을 썼다고 한다. 이 아티클에서는 그런 "악취나는 코드"에 대해서 정리해보겠다. 그리고 다음 아티클에서 악취 나는 코드들을 어떻게 해결할지 고민해본다.
+ +일단 리스트 부터 정리해본다. 이 리스트는 도서 리팩터링에 소개된 내용과 내가 만난 레거시를 더한 리스트이다. 전부 정리해본 다음 나름의 카테고리화를 진행해보겠다. + +1. 기이한 이름 +2. 중복된 코드 +3. 긴 함수 +4. 긴 매개변수 목록 +5. 전역 데이터 +6. 가변 데이터 +7. 뒤엉킨 변경, 산탄총 수술 +9. 기능 편애 (TODO : 객체지향 이야기로 묶을 수도 있을 것 같다.) +10. 데이터 뭉치들 +11. 기본형에 대한 집착 +12. 반복되는 Switch 문 +13. 반복문 +14. ~~성의 없는 요소들~~ +15. 추측성 일반화 +16. 임시 필드 +17. 메시지 체인 +18. 중개자 +19. 내부자 거래, 상속 포기 +20. 거대한 클래스 +22. 주석 +23. 길고 복잡한 조건문 +24. 위험한 리터럴 사용 +25. 어려운 도메인, 흩어진 상태들 +26. 위험한 Null Check + +### Temp - 주제 묶기 +주제를 크게 크게 묶을 수 있을 것 같다. 이곳은 그 후보들의 Temp이다. + +1. 가독성의 문제 (물리적으로 그냥 읽기가 어려움) +2. 의도 파악의 문제 (맥락을 모르는 사람이 의도를 파악하기가 어려움) +3. 변경 유연성을 낮추는 문제들 (객체지향적인 요소를 살리지 못해, 결합도를 높이고 응집도를 낮추는 코드들) +4. 예상하지 못한 위험에 노출되는 문제들 + + +# 1. Mysterious Name - 기이한 이름 +코드는 읽기 좋아야 한다. 읽는 사람은 이 메서드가 어떤 책임을 갖고, 어떻게 동작하는지, 메서드 안의 변수 하나 코드 한줄이 어떤 역할을 하는지를 최대한 쉽게 파악할 수 있어야 한다.
+ +## 1.1 누가? 무엇을? +이것을 가장 쉽게 달성하는 방법은 클래스와 메서드, 각종 변수들의 이름을 명확히 하는 것이다. 메서드는 "행동" 변수는 "행동 주체" 혹은 주고 받는 "상태"를 주로 맡게 된다. **이들의 이름이 아주 명확하다면 코드 누가? 무엇을? 하는지 파악하기가 쉬워진다.**
+**"어떻게"는 의도적으로 뺐다. 코드에서의 "어떻게"는 중요하지만, 나는 숨기는 것을 선호한다.** 예를 들어 함수 중간에 "예매하기" 코드가 들어가야 한다면, 나는 "예매하기" 메서드를 만들고 그것이 어떻게 동작하는지는 메서드 안에 캡슐화해 숨긴다.
+1. 그래야 의존성이 낮아진다. -> Caller는 어떻게 동작하는지를 몰라도 메서드를 이용할 수 있다. 인터페이스를 사용하는 핵심 이유이기도 하다 +2. 읽는 사람이 알아서 메서드 전체를 이해하고 싶을 때는 무엇을 하는지만 빠르게 파악하고, 어떻게 동작하는지를 파악할 때만 들어가볼 수 있기 때문이다. + +

+ +## 1.2 금융권 레거시 마이그레이션을 맡고 있는 내가 만난 사례들 +### 1.2.1 같은 내용 다른 표현 +결론적으로 함수, 모듈, 변수, 클래스는 매우 신경써서 이름을 지어야 한다. 읽으면서 의미를 파악할 수 있어야 하고, 최대한 표현과 스펠링이 같아야한다. 내가 근무하며 만난 코드 중에는 "한글"을 Hangul, 혹은 Hangual로 표현한 메서드를 봤었다. 같은 클래스는 아니였지만, 같은 패키지의 Util 클래스였다. +이 경우엔 이해하는덴 문제가 안 됐지만, 나는 금융권에 있다 보니 어려운 용어를 서로 다르게 쓸 때가 있어 난감했다.
+그래서 내 생각엔 둘 중 하나의 조치가 필요하다. +1. 합의된 용어집이 있어야 한다. +2. (가능하다면) IDE가 오타를 잡아주는 툴에 해당 단어를 추가한다. +3. 코드 리뷰가 필요하다. -> 잘못 쓰는 경우가 있으면 서로 봐주면 좋을 것이다. + + +### 1.2.2 최대한 단어를 줄이려는 노력 + +또한 내가 만나는 레거시들은 단어를 최대한 줄이려고 노력하고 있다. 경력이 30년 가까이 되는 분께서도 저에게 어떤 고객사는 단어를 절대 줄여 쓰지 못하게 했는데, 너무나도 불편했다고 말씀하셨었다.
+그만큼 긴 변수명이나 메서드명을 줄이는 것은 옛날엔 흔한 관례였던것 같다. 나는 항상 모든 일의 원인을 파악하려고 노력하는데, 예전의 불편한 모니터와 IDE상에선 아마도 긴 변수나 메서드명을 읽기 쉽지 않았을 것이다. Depth가 깊어지는 것을 크게 신경쓰지 않는 것과는 조금 대비되서 의아한 부분이다.
+ +또한 SI 업계는 다른 사람이 짠 코드를 읽을 일이 극히 드물기 때문에, 자신만 알아볼 수 있으면 괜찮은 경우가 많았을 것이므로, 작성하기도 편하고 짧아서 빠르게 읽을 수 있으므로 줄였을 것도 같다.
+ +단어를 줄이는 것이 항상 나쁜 것은 아니다. 이미 너무 잘 알려진 단어들은 줄여도 무방하다고 생각한다. Database를 DB로, Application을 App으로 줄여도 웬만해선 읽는 사람이 이해할 수 있을 것이다. 하지만, 평소 줄여 부르지 않는 용어를 줄이는 것은 문맥 파악에 큰 방해가 된다고 생각한다.
+ +그래서 안그래도 어려운 금융 용어를 줄여진채로 만나야 해서 파악하기가 매우 곤란한 상황이 많았다. 또한 같은 단어를 서로 다르게 줄일 때도 많아서 굉장히 혼란스럽다.
+ +**물론 무조건 verbose 하자고 하는 것은 아니다. 무조건 긴 변수나 메서드는 읽기 힘들다. 내 경험에 따르면 줄이려면 차라리 아래와 같이 줄였으면 좋겠다.** + +1. **합의된 줄임말을 사용한다.** +2. **단어를 줄이려면 중간에 자르는게 낫다고 생각한다. 아닌 경우 그냥 줄이지 않았으면 좋겠다. -> 검색 할 수가 없기 때문이다.**
+ +예를 들어 Select, Insert, Delete, Update를 줄여 Sel, Ins, Del, Upd라고 쓰는 경우를 정말 많이 봤는데, 이 경우엔 검색해서 찾기가 쉬웠다. 예를 들어 컨트롤 + F를 누르고 "Sel"이라고 치면 바로 찾을 수 있다. +(물론 레거시 코드를 읽기 시작한 초창기에 갑자기 마주친 "I"와 메서드에 쓰인 "Ins"를 보았을 때는 뭘 하고 싶었던 건지 이해하지 못했었다)
+ +하지만, Customer를 Cutm으로 줄이는 경우와 Insert를 Inrt로 줄이는 경우를 보았다. +나는 소스 코드에서 insert 하는 부분을 찾고 싶었는데, Inrt로 줄여 놓으니 Control + F로 전혀 찾을 수가 없었다. 어쩔 수 없이 긴 긴 코드를 다시 읽어야 했다. Cutm를 처음 마주친 사람은 이 뜻을 제대로 이해할 수 있을까?
+문제는 이런 줄임말이 정말 정말 많다는 것인데, **이러한 줄임말들은 읽는 사람이 이해하는 시간을 크게 늘린다.** + + +## 1.3 읽는 사람이 빠르게 이해할 수 있는 이름을 지어보자 +수능 비문학 문제를 풀며 모르는 단어가 나오면, 맥락을 통해 파악하곤 했다. 이게 가능했던 이유는 비문학 지문은 다양한 요소를 통해 맥락을 제공하기 때문이고, 단어가 어려울 뿐이지 추론할 장치들을 주었기 때문이다. ("하지만" 이후 이전 문장과 반대되는 내용이 오는 등..)
+ +**하지만 코드는 맥락이 변수와 메서드로 주어지는 경우가 많다. 아까 이들을 "행동"과 "주체" 라고 표현하지 않았는가?** 문장에서 행동과 주체를 파악할 수 수 없다면 맥락을 파악할 수가 없다..

+ +물론 제대로 된 이름을 짓는 것은 매우 어렵다는 것을 잘 알고 있다. 필 칼튼은 "컴퓨터 과학에서 가장 어려운 것은 딱 두가지이다. 캐시 무효화와 이름 짓기이다"라고도 말했었다.
+그래도 대게 **"무엇을"에 집중해** 머리를 싸메고 고민해보면 좋은 이름이 나온다 이 메서드는 무엇을 하려는 건지? 이 클래스는 어떤 책임을 갖는건지? 등 "무엇을"에 집중해 이름을 지으면 좋다. **그리고 코드 리뷰를 통해 여러 사람과 함께 고민하면 아주 멋진 이름이 나올 때가 많다.**
+**딱히 마땅한 이름이 떠오르지 않는다면 설계에 더 근본적인 문제가 숨어 있을 수도 있다.** 이 변수나 메서드가 맡은 역할이 너무 많아 하나의 이름을 짓기 어려운 것일 수도 있다. **나는 직원들에게 먹일 요리도, 하고 범죄자도 소탕하고, 돈을 받고 공연을 하기도 한다 나는 누구일까? 단 한마디로 표현할 수 없다.**
+ +### 결론 +1. 읽는 사람이 이해하기 쉬운 이름을 짓는다. +2. 여러 사람과 함께 고민하면 좋은 이름이 잘 나온다. +3. "어떻게"가 아닌 **"무엇을"에 집중한다.** **의도에 집중한다.** -> 이 메서드는 무엇을 하려 한다.. +4. 마땅한 이름이 떠오르지 않는다면 설계에 문제가 있을 수도 있다. + + +# 2. Duplicated Code - 중복 코드 +똑같은 코드 구조가 여러곳에서 반복된다면, 하나로 통합하여 더 나은 프로그램을 만들자. 물론 작은 차이점이 있을 수도 있으니 이에 주의하여 캡슐화 해보자.
+**운이 좋으면 파라미터로 만들 수도 있다.** 상태만 다른거라면 파라미터로 만들 수도 있고, 작은 단위의 행동만 다른 것이라면 람다를 활용할 수도 있다. 혹은 비슷한 부분만 일단 추출해보는 시도도 좋다.
+운이 더욱 좋아 같은 부모로 부터 파생된 서브 클래스의 코드들이라면, 상위 클래스에서 호출되게 할 수도 있겠다. + +1. 다른 부분이 없다면 메서드로 분리 +2. 다른 부분이 있다면 + 1. 다른 부분이 상태라면 다른 부분을 파라미터화 해서 해결할 수 있는지 확인한다. + 2. 행위라면 람다를 활용해본다. +3. 혹은 추출 가능한 부분만 분리하는 것만 해도 좋다. +4. 만약 같은 부모로 부터 파생됐고 내용이 같다면 상위 클래스로 옮겨 호출해도 좋을 것이다. + + +# 3. 긴 함수 - Long Funciton +## 3.1 왜 레거시 메서드들은 길까 +레거시 함수들은 왜 하나같이 무서울만큼 긴 것일까? 얼마 전에는 거의 만줄에 가까운 메서드를 마이그레이션 해야 하는 동료분의 이야기를 들었다.
+우선 앞서 언급했던 이유들과 같이 내가 읽을 수 있기만 하면 되어서 그랬을 수도 있다. 이런 당연한 이유를 빼고 고민해 봤을 때 결국 옛 방식에 익숙해서 그랬을 것이라는 결론이 났다. **결국 옛날 방식에 익숙해서 그랬을 것이다.** +1. 옛날의 절차지향 방식 + 변수 선언과 할당을 따로 하는 구조에 익숙하다 +2. 예전엔 서브 루틴 호출 비용이 커 짧은 함수를 피했다고 한다 (책에 따르면) +3. 옛날 IDE는 함수를 왔다갔다 하는게 매우 어려웠을 것이다. 따라서 짧은 메서드를 여러개 만드는 사람은 귀찮게 하는 사람이었을 것이다. + +
+ +## 3.2 주석을 달고 싶다면 메서드로 쪼게어라 + +마틴 파울러의 말에 따르면, 오랜 기간 잘 활용되는 프로그램들은 하나같이 짧은 메서드로 구성되어 있다고 한다. 이 메서드들은 서로가 서로에게 계속해서 연산을 위임한다. 여기에 이름까지 잘 지은다면 코드의 의도를 매우 파악하기 쉬워진다. 본문을 볼 필요도 없을 수도 있다.
+특히 현대 IDE의 힘을 빌린다면 함수와 함수 사이를 오가는 것은 그리 어려운 일이 아니다.
+**주석을 달고 싶다면 메서드로 쪼겐다.** 주석을 달고 싶다는 것은 작성하는 사람이 생각 했을 때, 코드가 아닌 코멘트가 있어야 이 코드를 더 쉽게 이해할 수 있겠다는 판단이 들었다는 것이다.
+ +메서드로 쪼게는 순간 일단 이름을 지어줄 수 있다. 주석을 달고 싶었던 부분에 대해 설명할 수 있게 되는 것이다. **코드의 의도와 목적을 드러낼 수 있게 되는데, 마틴 파울러는 원래 코드보다 더 길어지더라도, 코드가 단 한줄일지라도, 필요하다면 뽑아낸다고 한다.**
+ +## 3.3 함수 추출하기 +**함수를 짧게 만드는 작업의 99%는 함수를 추출하는 일이다.**
+함수를 추출하는 방법들을 알아보자. + +### 3.3.1 추출 함수의 매개변수가 너무 많은 경우 +만약 함수와 매개변수가 임시 변수를 많이 사용한다면 추출한 함수도 많은 매개변수가 있을 수 있어 더 난해해질 수도 있다. 그럴때 아래와 같은 방법을 권한다고 한다. + +1. `매개변수 객체로 만들기` : DTO를 만들거나 객체를 통째로 넘길 수 있다. - 만약 이전 메서드에서 DTO 객체의 값들을 뽑아서 메서드에 인수로 넣어주는 것이였다면, 그냥 다시 활용해도 좋다. 단, 계층간 혹은 메서드간 의도적으로 정보를 제한한 것이라면, 차라리 새로운 DTO를 만드는게 나을 것 같다.
(ex Member 객체가 가진 정보 5개 중 3개만 메서드의 인수로 들어가게 되고, 나머지 2개는 숨겨야 하는 상황에서 Member 객체를 보내는 것은 잘못됐다.) +2. `임시로 사용하는 변수 줄이기` : 매개 변수를 질의 함수로 바꾸어보라 임시 변수는 보통 어떤 코드의 결과값을 나중에 다시 잠조하기 위해 사용한다. 예를 들어 어어떤 계산 결과를 나중에 또 사용하기 위해 변수를 사용할 수 있는데 + + +### 3.3.2 조건문과 반복문을 추출해보자 +1. **조건문의 내용, Switch문의 Case 본문들을 하나의 함수로 추출할 수 있다.** 이렇게 하면 **A인 경우 치킨을 먹어라. B인 경우 피자를 먹어라와 같이 어떤 조건일 때 무엇을 할지 쉽게 드러낼 수 있다.** +2. Switch문이 여러개라면 조건부 로직을 다형성으로 바꿔라. +3. **반복문 안의 코드를 추출해 함수로 만들자** -> Java Stream에서 아주 많이 하는 일인데, 외부 반복을 내부 반복으로 바꾼다. + for문을 통해 List의 원소 하나 하나에 어떤 처리를 할 때, 코드가 길어질 수록 무엇을 하는지 정확히 파악하기 힘들다. **이를 메서드로 추출한다면 하나의 단어로 바꿀 수 있는 것이다.** + 순회하며 -> 값을 필터링한다, 하나 하나 객체로 변환한다, 어떤 처리를 한다 등 복잡한 for문에 대해 이해할 필요 없이 쉽게 의도를 파악할 수 있다. + **만약 반복문을 추출할 때 적합한 이름이 떠오르지 않는다면, 성격이 다른 여러 작업들이 반복문 안에 섞여 있는 것이다.** 이럴 때 반복문을 쪼갤 수 있다. + +### 3.3.3 메서드 길이를 줄이는 다른 방법들 +1. 임시 변수를 질의 함수로 바꾸기 : 보통 메서드 안에서 어떤 변수를 선언해 사용할 때는 어떤 결과를 메서드 내의 다른 곳에서 또 사용하기 때문이다. 만약 해당 변수를 만들기 위한 계산은 한번이고, 그 뒤엔 읽기만 한다면 클래스의 다른 메서드로 바꿀 수 있다. 특히, 다른 메서드에서도 이 변수를 활용한다면 메서드로 빼는 것이 좋은 선택이다.
예를 들어 어떤 어떤 양과 가격의 곱을 계산해 어떤 변수를 만든다고 생각해보자. 가격과 곱을 마침 Class가 맴버 변수로 가지고 있다면, 가격 계산 메서드를 따로 만들기 아주 좋다. +2. `함수를 명령으로 바꾸기` : 함수가 긴 이유가 응집도가 낮은 상황이라면, 아예 다른 클래스의 함수로 분리하라는 의미이다. 예를 들어 어떤 메서드 안에서 어떤 클래스가 하나의 역할을 할 때 이를 명령 클래스라고 부르기 때문에 이런 용어가 나왔다.
예를 들어 어떤 메서드에서 요리도, 하고 범죄자도 잡고, 노래도 부른다면 요리사, 경찰, 가수 클래스에게 각각 메서드를 주고 명령할 수 있다. + +
+ +# 4. Long Parameter List - 긴 매개변수 목록 +1. 매개변수 객체 만들기 : 많은 함수에서 사용되는, 세트로 몰려다니는 파라미터들이 있다면 하나의 객체로 만들어 두는 것도 좋다. (적당한 역할을 부여해줄 수 있다면) +2. 플래그 인수 제거하는 법 : (매개변수 하나를 줄이기 위한 조치로는 조금 과해 보이긴 한다. 감안하고 봐주시길..)
어떤 조건 단 하나로, 함수의 동작 방식이 바뀌는 경우가 있다. 이를 플래그 인수라고 한다. 예를 들어 손에 포크를 들고 있는지, 젓가락을 들고 있는지에 따라 먹는 방식이 달라지는 경우, 플래그는 식기이다.
플래그 인수는 잘 만들지 않으면, 어떤 플래그가 있고 플래그별로 어떻게 행동이 다른지 알기 어렵다고 한다.
예를 들어 아래 코드를 확인해보자. + +```java +deliveryDate = getDeliveryDate(order, true); + +... + +deliveryDate = getDeliveryDate(order, false); +``` + +여기서 메서드의 2번째 파라미터가 플래그로써 쓰이는데, 대체 이게 뭘 의미할까 + +```java +// 예제이기 때문에 냄새 포인트가 많아도 너그럽게 용서 바랍니다!! +public LocalDate getDeliveryDate(Order order, boolean isRush) { + if (isRush) { + int deliveryTime; + if (order.getState() == OrderState.MA || order.getState() == OrderState.CT) deliveryTime = 1; + else if (order.getState() == OrderState.NY || order.getState() == OrderState.NH) deliveryTime = 2; + else deliveryTime = 3; + + return order.getPlaceOn.plusDays(1 + deliveryTime); + } + + int deliveryTime; + if (order.getState() == OrderState.MA || order.getState() == OrderState.CT || order.getState() == OrderState.NY) deliveryTime = 2; + else if (order.getState() == OrderState.ME || order.getState() == OrderState.NH) deliveryTime = 3; + else deliveryTime = 4; + + return order.getPlaceOn.plusDays(2 + deliveryTime); +} +``` + +이 코드에서 일단 조건문을 분해해보자.
+ +```java +// 예제이기 때문에 냄새 포인트가 많아도 너그럽게 용서 바랍니다!! +public LocalDate getDeliveryDate(Order order, boolean isRush) { + if (isRush) { + return getRushDeliveryDate(order); + } + return getRegularDeliveryDate(order); +} + +private LocalDate getRushDeliveryDate(Order order) { + int deliveryTime; + if (order.getState() == OrderState.MA || order.getState() == OrderState.CT) { + deliveryTime = 1; + } + else if (order.getState() == OrderState.NY || order.getState() == OrderState.NH) { + deliveryTime = 2; + } + else { + deliveryTime = 3; + } + + return order.getPlaceOn.plusDays(1 + deliveryTime); +} + +private LocalDate getRegularDeliveryDate(Order order) { + int deliveryTime; + if (order.getState() == OrderState.MA || order.getState() == OrderState.CT || order.getState() == OrderState.NY) { + deliveryTime = 2; + } + else if (order.getState() == OrderState.ME || order.getState() == OrderState.NH) { + deliveryTime = 3; + } + else { + deliveryTime = 4; + } + + return order.getPlaceOn.plusDays(2 + deliveryTime); +} +``` +책에선 여기까지만 했는데, 더 나눠보면 좋을거 같다. +```java +// 예제이기 때문에 냄새 포인트가 많아도 너그럽게 용서 바랍니다!! +public LocalDate getDeliveryDate(Order order, boolean isRush) { + if (isRush) { + return getRushDeliveryDate(order); + } + return getRegularDeliveryDate(order); +} + +private LocalDate getRushDeliveryDate(Order order) { + int deliveryTime = getRushDeliveryTime(order.getState()); + return order.getPlaceOn.plusDays(1 + deliveryTime); +} + +private int getRushDeliveryTime(OrderState state) { + if (state == OrderState.MA || state == OrderState.CT) { + return 1; + } + if (state == OrderState.NY || state == OrderState.NH) { + return 2; + } + return 3; +} + +private LocalDate getRegularDeliveryDate(Order order) { + int deliveryTime = getRegularDeliveryTime(order.getState()); + return order.getPlaceOn.plusDays(2 + deliveryTime); +} + +private int getRushDeliveryTime(OrderState state) { + if (state == OrderState.MA || state == OrderState.CT || state == OrderState.NY) { + return 2; + } + if (state == OrderState.ME || state == OrderState.NH) { + return 3; + } + return 4; +} +``` + + +그 다음 아래와 같이 플래그를 없앨 수 있을 것이다. + +```java +// Before +deliveryDate = getDeliveryDate(order, true); +... 생략 +deliveryDate = getDeliveryDate(order, false); + + +// After +deliveryDate = getRushDeliveryDate(order); +... 생략 +deliveryDate = getRegualarDeliveryDate(order); +``` + +이렇게하면 더 의도도 잘 드러나고, 파라미터가 많은 경우 파라미터도 줄일 수 있다. + +# 5. 전역 데이터와 가변 데이터 +**마틴 파울러는 전역 변수와 가변 데이터에서 가장 지독한 악취가 풍긴다고 표현한다.** + +전역 변수와 가변 데이터를 사용하는 것의 문제는 아래와 같다. +1. 클래스 내의 어디에서든 값을 변경할 수 있다. +2. 그리고 이 변경에 대한 추적이 안된다. + +대상은 각종 가변 데이터들과 전역 변수, 클래스 변수 (클래스에서 static 으로 선언된 변수), 싱클톤 객체 모두에게 해당된다.
+ +마틴 파울러는 변수를 캡슐화 하여 Getter나 Setter를 제공할 것을 권했다. 그리고 범위를 최대한 좁힐 것을 권했다. 예를 들어 Setter가 다른 기능들을 가진 메서드에 섞여 있어 중간에 호출된다면 추적하기 어려울 것이다.
+ +이를 통해 변수를 수정하는 부분을 쉽게 찾을 수 있고, 범위를 제한하기 쉬울 것이다.
+단지 변경 메서드를 호출하는 곳을 찾아내면 쉽게 추적할 수 있는 것이다.

+ +## 5.1 불변 객체 (중요 :star:) + +// TODO : 예제와 쓰레드 세이프 관련 정보 추가 + +**만약 변경되어선 안 되는 데이터라면 변할 수 없게 만들자.**

+ +Java에서 불변성을 구현하는 방법엔 여러가지가 있다. **한번 할당되면 변할 수 없도록 `final` 키워드를 붙여주는 방법이 대표적이다.**
+ +`final` 키워드를 변수에 붙이면, 재할당이 불가능하고, 메서드나 클래스에 할당되면 상속이 불가능하다.
+음.. 그냥 Getter로 제공하면 변경 불가능 한것 아닌가?
+ +아니다 래퍼런스 변수를 Getter로 공유할 때 `final`을 붙이지 않는다면, 해당 변수에 새로운 객체를 할당할 수 있을 것이다.

+ +**그럼 `final`만 붙이면 만사 해결일까?**
+아니다! 만약 공유되는 데이터가 컬렉션이라면 단지 `final`만 붙인다고 불변을 보장하지 못한다.
+무슨 이야기일까? `final`은 단지 변수에 값을 재할당 하는 것을 막아주는 키워드로 컬렉션에 원소를 추가하거나 지우는 것까지 막아주지는 못한다.
+**공유된 List를 호출한 쪽에서 마음대로 변경할 수 있게 되는 것이다!** **이럴 땐, 값을 복사해서 복사본을 전달하면 문제가 해결된다!**
아주 다양한 방법이 있는데, 리스트의 경우 `new ArrayList<>(컬렉션)`나 `Arrays.asList()`와 같은 문법을 활용하는 방법도 있지만 `List.of()`를 활용하는 것을 추천한다.
+ +**앞의 두 문법으로 생성된 컬렉션은 변경할 수 있고, `List.of()`로 생성한 컬렉션은 불변이다!**
+또한 인수로 Null값이 들어오는 것을 막아준다.
+ +받은 쪽에서도 이 리스트를 활용할 떄 불변으로 활용할 수 있으면 좋은데, Thread Safe하기 때문이다. `List.of`로 만든 결과물은 원소를 추가하거나 삭제하는 경우 UnsuportedOperationException 예외를 발생시킨다. + + +# 7. 뒤엉킨 변경, 산탄총 수술 +객체지향은 소프트웨어의 구조를 변경하기 쉬운 상태로 만드는 것에 큰 가치를 준다. **코드를 수정할 때 딱 한군데만 수정해도 된다면 너무나도 행복할 것이다. 이는 의존성이 낮고, 응집도가 높을 떄 가능하다.**
+반대되는 상황에선 "뒤엉킨 변경, 산탄총 수술" 케이스 중 하나의 냄새가 풍긴다. "뒤엉킨 변경"은 **SRP가 지켜지지 않은 상황을 가리킨다. 응집도가 낮은 케이스인데, 하나의 모듈이 서로 다른 이유들로 다양하게 변경될 때를 이야기한다.** (즉, 하나의 모듈이 다수의 책임을 가지고 있다.)
+ +예를 들어 나는 강력 범죄자도 잡고, 고급 초밥을 만들어 돈도 버는 사람이다. 이때 갑자기 식당 컨셉이 바뀌어 오늘 부터는 돈까스를 팔아야 한다. 그럴때 나는 변화가 이루어질 수 밖에 없다. 혹은 오늘 부터 강력 범죄자를 구속할 때 수갑 대신 밧줄을 이용해야 한다고 하자. 그러면 나는 또 변화가 필요하게 된다.
+문제는 나라는 클래스 안에 범죄자를 잡기 위한 메서드들과 요리를 하기 위한 메서드들이 뒤엉켜 있기 때문에 이 변화를 만드는 사람들은 사진과 아예 무관한 코드 맥락들까지도 어느 정도 이해하고 있어야 한다.
+개발 초반에는 경계가 모호하기 떄문에, 뒤엉킨 변경을 만들기 쉬우나, 개발하는 과정에서 꼭 쪼게어 주어야 한다.
+두 맥락 모두에 필요한 메서드나 상태가 있다면 먼저 분리하라. 이때 필요한 상태들이 있다면 메서드 인수로 전달할 수 있도록 정의하면 될 것이다. 그리고 필요하다면 모듈을 나눠라 하나의 모듈이 하나의 책임만 가질 수 있게 만들어라. 그리고 그 모듈이 가진 책임에 따라 이름을 정해주면 될 것이다. 모듈은 클래스일 수도 있고, 패키지일 수도 있고, 무엇이든 될 수 있다.


+ +산탄총 수술은 비슷하면서 반대이다. 뒤엉킨 변경이 응집도가 낮은 케이스였다면, **산탄총 수술은 의존도가 높은 케이스이다.** 응집도가 낮으면 하나의 모듈이 여러 이유로 변경되었다.. **의존도가 높은 케이스는 반대로 하나의 기능을 수정하면, 여러 모듈에 변경이 필요해진다.**
+ +이럴 떄는 함께 변경되는 대상들을 한 모듈에 묶어두거나, 비슷한 데이터를 대상으로 연산하는 메서드들을 한 클래스에 모아두면 좋을 것이다. **그렇게 하면 해당 데이터의 구조나 특징이 변경 되었을 때, 하나의 클래스만 변경해도 될 것이다.**
+**또 어설프게, 불필요하게 분리된 함수나 클래스를 다시 불러오는 것도 좋은 방법이다.** 너무 불필요하게 작게 여러 클래스에 나뉘어진 로직을 원래 위치했던 메서드나 클래스에 다시 불러와 한군데에 모으는 것도 좋은 방법일 수 있다.
+ +라이브로 개발하는 과정에서는 충분히 과도하게 분리할 수도 있다. 나중에 그것이 과하단걸 알았다면, 그때 다시 합치면 된다. + +# 8. Feature Envy 기능 편애 +코드를 모듈화 하는 것은, 함께 변경될 가능성이 높거나, 비슷한 역할을 하는 요소들을 최대한 묶어 내는데에 목적이 있다.
+그런데 기껐 모듈을 묶었는데, 모듈 내부가 아닌 외부 모듈과 더 많은 소통을 하는 경우가 있다. 기능 편애는 어떤 함수가 자기가 속한 모듈의 함수나 데이터 보다 다른 모듈의 함수나 데이터와 상호작용할 일이 더 많을 때 풍기는 냄새로, 외부 데이터들과의 소통이 잦은 객체나 함수를 그 쪽으로 옮겨준다.
+설명이 조금 모호하게 느껴질 수 있는데, 예를 들어 유저 도메인에 속한 메서드임에도 불구하고, 유저에 대한 정보가 아닌 "쿠폰" 정보를 열심히 다루는 메서드들이 있을 수 있다. 이런 경우 모듈화 하여 분리하라는 것이다.

+ +핵심은 변경시 함께 변경되는 대상을 모으는 것이다.
+ +# 9. 데이터 뭉치 +몰려다니는 상태를 매번 한대 묶지 않고 사용하는 패턴이다.
+예를 들어 나라, 시, 동, 건물, 호수 등의 정보를 통해 어떤 하나의 주소 정보를 묘사할 수 있다. 이때, 이 값들을 묶어 "Address"로 관리할 수 있으면 묶어서 관리해도 괜찮을 것이다.
+예시를 주소로 들어 매우 명확하지만, 사실 처음엔 고려하지 못했는데 쓰다보니 데이터들이 뭉쳐 다니는 경우가 있다.
+**판단 기준이 모호할 때는 값 하나를 삭제해보자. 만약 나머지 데이터만으로는 의미가 없어져 버린다면, 이는 객체로 모아봐도 좋다는 뜻이다.** 이러한 리팩토링을 통해 +1. 연관된 상태를 한번에 관리할 수 있고 +2. 의미를 붙여줄 수 있으며 (클래스나 메서드에 이름을 붙여주면서) +3. 메서드 전달시 매개변수를 줄일 수도 있다. + +
+ +또한 모아진 상태들과 관련된 메서드들이 새로운 클래스에 옮겨질 수 있는지 검토해보자! 모아진 상태들을 다루는 메서드들이 있었고, 이들을 클래스 내부로 옮긴다면 상태들을 품은 클래스가 스스로 상태를 조작하고, 판단하게 만드는 것이다. 예를 들어 어떤 호텔에 지금 예약 가능한지 확인하기 위해 호텔 객체에게만 물어볼 수 있는 메서드만 구현한다.
+이렇게 하면 호텔 예약 가능 여부는 호텔 스스로가 판단할 수 있고, 관련 정책들을 안으로 숨길 수가 있다. 그리고 정책이 바뀔 때는 단순히 호텔 객체에서 정책을 담당하는 부분만을 바꾸면 될 것이다.
+**외부에서는 어떤 일이 일어나는지 신경 쓰지 않아도 된다!**
+**이로써 응집도는 높히고, 의존성은 낮출 수 있게 되는 것이다.** + + +# 10. 기본형 집착 +**기본형을 객체로 만드는 것을 너무 주저하지 말자.**
+금액, 전화번호, 구간 등을 어떻게 표현할 것인가? 전화번호의 경우 "010-7777-7777"처럼 String으로 나타낼 수도 있고, 01077777777처럼 숫자로 나타낼 수도 있다. + + +### 1. 기본형에 이름을 붙여줄 수 있다. +예를 들어 어떤 메서드에 String 파라미터 4개가 있다고 생각해보자. 각각 금액, 금액 단위, 전화번호, 좌표를 의미한다고 하자. 만약 모두 String 형이고, Kotlin의 파라미터마다 이름을 지정할 수 있는 Named Parameter와 같은 기능이 없다면 작은 순서 실수만으로도 큰 문제가 생길 수도 있다. 또한 그 실수를 찾기도 힘들 것이다.
+하지만 각각을 래핑한 Type이 있다면 어떨까? 금액과 단위를 묶은 Money 클래스, 전화번호 클래스, 좌표 클래스가 있다면, 이 메서드에 인자를 전달해줄 때 실수할 일이 없어 좋을 것이다.
+ + +### 2. 클래스 추출의 힌트가 된다. +위의 예시를 통해 설명해보자면, 돈의 금액과 단위를 통해 계산하는 로직, 전화번호가 유효한지 확인하는 메서드, 좌표간 계산 로직 등을 리팩토링 이후 각각에 클래스에 넣어줄 수 있다.
+이전에는 계산을 담당하는 외부 util 클래스가 String이나 int 등의 기본 자료형을 받아 활용하는 등의 방법 밖에 없었으나, 클래스로 추출하게 되면서 스스로 판단할 수 있는 기회를 준다!
+또! 또 의존도가 낮아지고, 응집도가 높아졌다! 너무 좋다.
+ +**기본형을 객체로 만드는 것을 너무 주저하지 말자.** + +# 11. 반복되는 switch 문 + +어떤 사람들은 switch문은 모두 사악하다고 말한다. 이들은 switch 문은 모조리 조건부 로직을 다형성으로 바꿔 없애야 할 대상이라고 주장한다.
+ +하지만 핵심은 중복된 switch 문이 문제가 되는 경우는 조건절을 하나 추가할 떄마다 다른 switch문도 영향을 받을 때이다. 이럴 때는 switch문을 다형성을 통해 바꾸는 것이 적절하지만, 무조건 switch가 나쁘다고 할 것은 아니다.
+문제가 있는 경우엔 아래와 같이 바꿔볼 수 있다. + +## 11.1 조건부 로직을 다형성으로 바꾸기 (Replace Conditional With Polymorphism - 10.4 패턴) + +동물들이 각각 자신의 우는 소리를 출력하게 하는 프로그램을 만든다고 생각해보자.
+switch를 활용해 아래와 같이 구현할 수 있다. + +```java +static Animal { + + Type type; + + public void 울기() { + switch (type) { + case 소 -> System.out.println("음모~"); + break; + + case 강아지 -> System.out.println("멍멍~"); + break; + + case 고양이 -> System.out.println("야옹~"); + break; + + default -> System.out.println("난 울지 않아!"); + } + } +} +``` + +이러한 switch문이 항상 나쁜건 아니다. 언제 나쁜지를 알아야 한다.
+기본 Type을 묘사해 처리할 것이 아니라면, default는 사실 꽤나 괜찮은 도구에 속한다. 성능적으로도 문제 없고, 간단한 경우 가독성에도 큰 문제는 없다.
한번 말해보자. 어떤 상황에서 switch가 나쁜가? + +1. 위와 같이 동물 Type에 따라 다른 동작을 수행하게 만드는 switch문이 여러개인 상황 +2. 새로운 Type이 추가된다. + +왜 나쁜가?
+이 경우, **새로운 Type이 추가될 때 해당 타입을 검사하는 모든 switch를 찾아 계속해서 케이스를 추가해 줘야 한다.** 이는 실수하기 쉽고 귀찮다. 처리하지 않은 케이스는 전부 default로 향해버린다.
+이 경우 다형성을 활용할 수 있다. + +```java + class Cow extends Animal { + + @Override + public void 울기() { + System.out.println("음모~"); + } + } + + class Dog extends Animal { + + @Override + public void 울기() { + System.out.println("멍멍~"); + } + } + + class Cat extends Animal { + + @Override + public void 울기() { + System.out.println("야옹~"); + } + } +``` + +Animal을 추상 메서드로 두고, 각 메서드가 스스로 울음을 구현하도록 하는 것이다. 이렇게 하면 + +1. 새로운 Type이 추가될 때 "울기" 구현이 강제된다. 따라서 실수로 빼먹을 일이 줄어든다. Animal을 쓰는 곳에선 단지 "울기"를 호출하면 되는 것이다. +2. 상태가 모여있다. 기존처럼 switch로 묶여있을 때 보다 더 직관적으로 수정할 수 있다. 그럴 일은 없겠지만 고양이가 우는 방식이 바뀌게 되는 경우 나는 고양이 클래스의 울기 메서드만 찾아서 변경하면 된다. + +사실 위 경우는 나름 상태가 모여 있는 케이스였다. 한번 리팩토링을 한 것이다. 만약 "울기" 메서드가 Animal 밖에 있었다면 수정시 더 많은 고생을 했어야 했을 것이다. + +## 11.2 더 간단하게 + 상태를 강제 +상태를 강제하려면 어떻게 할까? 예를 들어 클래스에 "울음 소리" 상수를 강제하고 싶고, 이 값은 밖에서 꺼내 쓸 수도 있다. 이 경우 단순히 "울기" 혹은 "get울음소리"를 사용하는 것 보다 Enum을 활용할 수 있다.
+ +Enum의 변수에 "울음 소리"를 넣는 경우 Type에 새로운 Enum을 추가할 때 "울음 소리" 값을 강제로 정해줘야만 하는 것이다. 이 방법은 상태를 모으기 아주 좋은 방법이다. 더 알아보고 싶다면 [이 글](https://jojoldu.tistory.com/137)을 참고해 보면 좋을 것이다. + + + +# 12. 반복문 +일급 함수가 지원되는 언어라면, 반복문을 파이프라인으로 변환하는 것을 고려하라.
+ +내부 반복을 사용하는 반복문 대신 파이프라인으로 변환하는 것엔 여러가지 장점이 있다. 내가 예전에 아주 정성 들여 쓴 글에 잘 정리 해두어서 글을 첨부한다. [Lambda & Stream의 도입 배경과 원리, 최적화 전략](https://dwaejinho.tistory.com/entry/Java-Lambda-Stream-%EB%8F%84%EC%9E%85-%EB%B0%B0%EA%B2%BD%EA%B3%BC-%EC%9B%90%EB%A6%AC-%ED%8C%8C%ED%95%B4%EC%B9%98%EA%B8%B0) + +

+ +# 13. 추측성 일반화 + +나중에 필요할 것으로 생각되어, 지금 당장은 필요 없는데 작성해 놓은 코드들을 말한다. 그 결과물은 무엇인가? 나중에 결국 편리해졌는가
+작든 크든 프로젝트를 몇 번 만들어본 사람은 공감할 것이다. **사실 나중에도 딱히 필요 없는 경우가 꽤 있다. 결국 용도가 없으니 맥락 없이 이 코드를 사람은 이해할 수 없고, 또 관리하기 어려워진다. 예를 들어 새로 만드는 기능들이 이 코드에 영향을 받을 수도 있다.**
+나도 한번도 생각해보지 못했다가 이 책을 읽으며 돌아봤는데 이런 경우가 꽤나 많았다.
+꼭 필요할 때만 만들어야 한다는 YANGI는 이러한 상황에서 왔을 것이다(라고 나는 생각한다.)
+ +이렇게 하자 + +1. 쓸대없이 다른 클래스나 메서드에 역할을 위임했다면 다시 데려오자.
그 과정에서 불필요하게 상속 관계가 만들어지고 하위 클래스가 만들어졌다면 지우자! (객체지향적 이점이 딱히 없는 상황이라면!) +2. 사용되지 않는 매개변수는 없애버린다. +3. 이후 테스트 케이스가 있다면 같이 삭제한다. + + +# 14. 임시 필드 TODO : 구체적인 예시 수집 +특정 상황에만 값이 할당되는 필드가 있다. 평소에는 null로 있다가 필요한 몇 상황에서만 null이 아닌 제대로 된 값이 할당되는 경우이다.
+이러한 필드를 만들 당시에, 충분한 논의가 있었더라면 아마 만든 사람 끼리는 필드가 있는 이유를 정확하게 알 것이다. 하지만 그 맥락을 모르는 사람에게는 어떤가?
+이 필드를 왜 사용하고, 언제 사용하는지 파악하기 어렵다. 만약 이름을 잘 지어 용도를 알더라도, 쓰는 입장에서는 이 값이 항상 채워져 있을 것이라고 생각할 수 밖에 없다.
+이러한 경우 보통 클래스를 추출하면서 적절한 위치를 찾아줄 수 있다. 이 필드가 들어가야할 곳을 찾던지, 클래스로 추출한 다음 관련 함수들도 옮겨 캡슐화 해준다. 이 필드가 + +## 14.1 특이 케이스 추가하기 패턴 +// TODO : 내용 더 채워보자. + +어떤 특이 케이스에 대한 동작을 클래스로 추출한다면, 상태 확인 -> 동작 코드를 단순한 함수 호출로 대체할 수 있다. 예를 들어 아래 Before, After 와 같이 구현할 수 있다. + +```java +// Before +if (customer == MemberType.Guest) { + this.customerName = "미가입 고객"; +} + +// After +class UnknownCustomer extends Customer { + private static final String NAME = "미가입고객"; + + ... + + UnownCustomer(String name) { + super(NAME); + } +} +``` + +After와 같이 구현하면, getName 호출시 미가입 고객이 출력된다.
+이렇게 특이케이스에 대한 클래스를 따로 만들어 주는 방식도 고려해볼 수도 있다. + +# 15. Message Chains, Data Class + +메시지 체인이란 어떤 A라는 객체가 B라는 객체를 얻은 뒤, 또 B에게 C를 요구하는 등 객체들에게 요청하는 작업이 Chain의 형태로 계속 이어지는 경우를 말한다.
+이러한 상황은 A측에서 B와 C의 내부 상태를 너무 구체적으로 알게 되는 문제가 있다. **객체의 자율성을 뺐어** 객체가 아닌 Data 래퍼 객체에 지나지 않게 다루고, **결국 결합도가 높아지고, 응집도가 낮아진다.** 왜냐하면 C가 변경되면 A에가 B, C를 호출하는 코드도 변경되어야 해서 결합도가 높은 것이고, A가 C의 내부 동작까지 인지하게 되며 응집도가 낮아졌다.
+ +이 문제는 Data Class에서도 똑같이 발생할 수 있는데, 데이터 저쟝 용도로만 쓰이다 보니 외부에서 객체를 너무 깊숙히 알게 될 수도 있다.
+ +이러한 문제를 피하기 위한 캡슐화 지침으로 "디미터 법칙"이 있다. 예전에 디미터 법칙에 대한 글을 열심히 정리하고 발표한 적이 있다. 아래 글을 참고하자.
+[Law Of Demeter](https://dwaejinho.tistory.com/entry/Law-of-Demeter-1) + +
+ + + +# 16. 중개자 Middle Man + +캡슐화는 객체를 만드는 훌륭한 이유 중 하나이다. 앞에서도 여러번 나왔지만, 캡슐화를 통해 우리는 응집도를 높히고, 결합도를 낮추는 등의 많은 이득을 얻을 수 있다.
+우리는 스스로 너무 많은 책임을 가질 필요가 없다. 나는 강력 범죄자를 소통하고, 고급 초밥을 요리해 돈을 벌며, 가끔 공연장에서 노래를 부른다.
+내 몸을 딱 여러개로 나눌 수 있다면 중개자를 만들면 좋을 것이다. 범죄자를 잡는 나, 고급 초밥을 만드는 나, 공연장에서 노래를 부르는 나를 분리해 이들에게 "부탁"하는 것이다.
+이렇게 하면 + +1. 결합도가 낮아진다 : 갑자기 생선이 씨가 말라 대체육 초밥을 만들어야 하는 세상이 왔다. 만약 하나의 클래스에 3가지 행위들이 모여있었더라면 코드를 고치는 입장에서 다른 쓸대없는 코드를 이해해야 했다. 세 분야 중 하나만 변경 되어도 다른 분야들의 코드들에도 영향이 간다. +2. 응집도가 높아진다 : 노래와 관련된 상태는 오직 가수 클래스가 갖게 한다. 요리와 관련된 상태는 오직 초밥 요리사가 갖게 한다. 이렇게 하면 자신과 무관한 상태를 갖고 있는 경우가 작아진다. + +바로 위인 15번 메시지 체인에 수록한 글을 다시 첨부한다. 이 글에 이러한 캡슐화가 어떻게 결합도를 낮추고 응집도를 높히는지, 이렇게 하는 것이 왜 객체지향에서 중요하고 변경 유연성을 높히는지 아주 자세하고 이해하기 쉽게 쓰려 노력했다. [Law Of Demeter](https://dwaejinho.tistory.com/entry/Law-of-Demeter-1) + +
+ +물론 지나치게 중개자를 사용하는 것도 좋지 않다. 어쩔 때는 직접 소통하게 두는 것이 낫다. 이러한 상황은 어떻게 판단할까? 위에 첨부한 글에서 "디미터 법칙과 객체지향 설계는 무적일까?" 부분을 보시면 참고가 될 것 같다. + + +# 17. 내부자 거래, 상속 포기. +#### 내부자 거래 +상속 구조에서는 부모 자식 사이에 은밀한 결탁이 생길 때가 있다. 이러한 내부자 거래 때문에 하위 클래스는 상위 클래승의 너무 많은 것을 알려고 하게 된다. 또! 결합도가 높아지려고 한다..
+ +#### 상속 포기 +하위 클래스에서 상위 클래스의 메서드나 데이터를 상속 받고 싶지 않을 때가 있다.
+예전에는 이를 계층 구조 설계 자체를 잘못 한 것으로 파악하고, 물려 받지 않을 코드들을 다른 객체로 분리해, 물려 받을 내용만 남겨진 상위 클래스를 상속하는 방식을 권했다고 한다.
+마틴 파울러는 이 방법을 항상 권하지는 않는다고 한다. 주로 하위 클래스가 상위 클래스의 행위는 필요로 하지만, 인터페이스 전부를 따르고 싶지 않을 때 이런 상황이 발생하는데, 인터페이스를 따르지 않을 거면 왜 상속 받는가..
+ +**이 두 상황에서 서브 클래스를 위임으로 바꾸거나, 슈퍼 클래스를 위임으로 바꾸는 방식으로 상속 메커니즘에서 벗어날 수 있다.** + +## 17.1 서브 클래스를 위임으로 바꾸기 +// TODO : 채우기 + +## 17.2 슈퍼 클래스를 위임으로 바꾸기 + + +# 18. 거대한 클래스 + +너무 거대한 클래스는 너무 많은 책임을 갖고 있는지도 모른다. 하나의 클래스가 너무 많은 필드와 (상태) 메서드 (행위)를 가지고 있다면, **혼자서 여러 책임을 갖고 있는 것은 아닌지 검토한다.**
+ +1. 많은 책임을 갖고 있다면, 결합도와 응집도를 고려해 분리할 수 있는만큼 분리해본다. +2. 상위 클래스 서브 클래스로 나눌 수 있다면 나눈다. +3. 필요하다면 Enum을 갖고, Enum에 따라 다른 행위를 하도록 구현하는 대신 하위 클래스로 분리할 수도 있다. +4. 사용하지 않는 필드가 있는지 검토하고 있다면 분리하라 (추측성 일반화 참고) + + +# 19. 주석에 관하여... +주석은 악취인가 향기인가.
+주석은 향기이다. 그러나 문제는 주석을 "탈취제"처럼 사용하는 데에 있다. 악취가 나는 코드를 덮으려 주석을 사용하는 것이라면 그만 둬야 한다.
+ +**어떤 코드 블록에 주석을 남기고 싶다면, 어쩌면 당신은 메서드 분리가 필요할 수 도 있다.** 메서드를 분리하면서 당신은 + +1. **동작들에게 이름을 붙여줄 수 있다!!** -> 주석이 필요없어 지는 것이다. +2. **어쩌면 하나의 메서드가 너무 많은 책임을 갖고 있었을 수도 있다** 메서드 이름만으로 설명이 불가능 했다는 것은 그 안에 또 다른 역할을 하는 코드들이 있었다는 힌트가 될 수 있다. + +

+ +만약 이렇게 하더라도 주석을 달고 싶다면, 다른 방법들을 고려해볼 수 있다. +1. 함수 선언 바꾸기로 이름을 바꾼다. +2. 이 시스템이 정상적으로 동작하기 위한 **선행 조건들을 명시하는게 목적이였다면** -> 어셔선 추가하기 + +주석을 남기고 싶은가? 일단 주석이 필요없는 코드로 리팩토링 한다. +이제 코드엔 악취가 제거된다. 이 모든 조치를 취하고도 주석이 필요하다면 이제 주석은 방향제가 되는 것이다.
+어쩌면 당신이 주석을 달고 싶은 이유는 합리적일 수도 있다. + +1. TODO를 코드 레벨에서 남기고 싶을 때. +2. 무언가 이유를 설명하는 주석을 달고 싶을 때. +3. 확실하지 않은 부분을 공유하고 싶을 때. + +가끔 주석은 코드를 여행하는 히치하이커에게 도움을 줄 수도 있다.