<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>rkaehdaos in the purpleland</title>
    <link>http://rkaehdaos.github.io/</link>
    
    <atom:link href="http://rkaehdaos.github.io/rss2.xml" rel="self" type="application/rss+xml"/>
    
    <description>rkaehdaos&#39;s blog (감동맨의 블로그)</description>
    <pubDate>Mon, 19 Sep 2022 05:28:03 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>[Clean Code] 다 읽었다~</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-review/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-review/</guid>
      <pubDate>Sat, 19 Feb 2022 02:31:22 GMT</pubDate>
      
        
        
      <description>&lt;h1 id=&quot;클린-코드-회고&quot;&gt;&lt;a href=&quot;#클린-코드-회고&quot; class=&quot;headerlink&quot; title=&quot;클린 코드 회고&quot;&gt;&lt;/a&gt;클린 코드 회고&lt;/h1&gt;&lt;p&gt;책 하나를 이렇게 깊게 파고든 것은 토비의 스프링 이후 거의 10년만에 처음이지 않</description>
        
      
      
      
      <content:encoded><![CDATA[<h1 id="클린-코드-회고"><a href="#클린-코드-회고" class="headerlink" title="클린 코드 회고"></a>클린 코드 회고</h1><p>책 하나를 이렇게 깊게 파고든 것은 토비의 스프링 이후 거의 10년만에 처음이지 않나 싶습니다.</p><p>사실 공부하다 궁금한 점이 있으면 검색을 해가며<br>다른 블로거 몇몇의 클린코드 정리 한 포스팅을 보았는데<br>전부 책내용을 복사 붙이기가 태반이라 전혀 궁금증이 해소되지 않았습니다.</p><p>그래서 검색하다가 그냥 지쳐서.. 그냥 전부 해보기로 했습니다<br>그냥 책만 보고 쭉 넘어가는게 아니라 깃헙 저장소 만들고<br>책에 나온 예제는 진짜 100% 전부 실행해 보느라 진행이 굉장히 오래 걸렸습니다. </p><p>사실 대다수의 코드는 전부 깃헙 저장소에 넣어놨으며<br>잊어먹을때쯤 한번씩 공부하는 것도 나쁘진 않을 것 같습니다</p><p>왜 많은 사람들이 추천했는지 알 것 같습니다</p><p>필독서, 명서 이런 타이틀이 붙은 책들은 약간 고전이란 생각이 먼저 들어서<br>크게 도움이 될까 하는 생각이 들었지만 역시 명불허전..</p><p>요새 나오는 책들은 좋은 책들이 많아서<br>이런 필독서나 명서들의 필수 에센셜한 내용들이 추가가 되어 있어서<br>사실 보는 내용들은 처음 보는 내용들은 아니었지만 </p><p>흘러가는 게 아니라 클린 코드라는 컨셉 내에서 여러 내용을 다루다보니<br>머리 속에서도 그동안 알았던 내용들이 클린 코드의 컨셉으로 정리가 되면서<br>훨씬 직관적으로 정리가 되었습니다.</p><p>이 책을 읽으면서 느낀점은<br>이책이야 말로 같은 책을 읽어도 얻어가는 것은 사람마다 다를 거라는 생각이 들었습니다</p><blockquote><p>후임 주니어와 같이 일정을 잡고 책을 보고 챕터를 볼때마다<br>리뷰를 했는데 전혀 이해를 못하더라고요. 그냥 다 알고 있는 내용이라고<br>대충 넘어가던데. 다 아는데 왜 읽기 전과 후가 코드 상태가 그대로인지 ㅠ</p></blockquote><blockquote><p>역시 이번에도 책에서 오류 발견.<br>개인적으로는 대충 flow로 흐르지 않고 찬찬히 읽는 버릇이 있다보니<br>오타나 오류를 잘 발견하곤 합니다.<br>엄청나게 많이 읽힌 책이고 벌써 3판인데도 오류가 있더라고요<br>정오표에도 없는 오류라서 인사이트에 신고하니<br>다음쇄에 반영하겠다며 감사하다고 답메일이 왔습니다<br>뿌듯합니다.</p></blockquote><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-review/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] XVII. 냄새와 휴리스틱</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch17/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch17/</guid>
      <pubDate>Wed, 09 Feb 2022 01:57:47 GMT</pubDate>
      
        
        
      <description>&lt;h1 id=&quot;XVII-냄새와-휴리스틱&quot;&gt;&lt;a href=&quot;#XVII-냄새와-휴리스틱&quot; class=&quot;headerlink&quot; title=&quot;XVII. 냄새와 휴리스틱&quot;&gt;&lt;/a&gt;XVII. 냄새와 휴리스틱&lt;/h1&gt;&lt;p&gt;냄새&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;마틴파울러가 말</description>
        
      
      
      
      <content:encoded><![CDATA[<h1 id="XVII-냄새와-휴리스틱"><a href="#XVII-냄새와-휴리스틱" class="headerlink" title="XVII. 냄새와 휴리스틱"></a>XVII. 냄새와 휴리스틱</h1><p>냄새</p><ul><li>마틴파울러가 말한 나쁜 코드의 조짐</li><li>리팩토링 책에서는 켄트백이 당시 애기 키우면서 기저귀 냄새때문에 그렇게 표현했다고 읽은것 같음</li></ul><h2 id="전체-리스트"><a href="#전체-리스트" class="headerlink" title="전체 리스트"></a>전체 리스트</h2><h3 id="주석-Comment"><a href="#주석-Comment" class="headerlink" title="주석 - Comment"></a>주석 - Comment</h3><h4 id="C1-부적절한-정보"><a href="#C1-부적절한-정보" class="headerlink" title="C1(부적절한 정보)"></a>C1(부적절한 정보)</h4><ul><li>주석은 코드,설계에 기술적인 설명만 부연</li><li>변경이력, 기록등은 주석말고 형상관리에 적어라.</li></ul><h4 id="C2-쓸모-없는-주석"><a href="#C2-쓸모-없는-주석" class="headerlink" title="C2(쓸모 없는 주석)"></a>C2(쓸모 없는 주석)</h4><ul><li>오래된 주석, 엉뚱한 주석, 잘못된 주석</li><li>최대한 빨리 삭제 하자</li></ul><h4 id="C3-중복된-주석"><a href="#C3-중복된-주석" class="headerlink" title="C3(중복된 주석)"></a>C3(중복된 주석)</h4><ul><li>코드 설명 충분한데 구구절절 설명하는 주석<figure class="highlight js"><figcaption><span>중복된 주석 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">i++; <span class="comment">//i 증가</span></span><br></pre></td></tr></tbody></table></figure></li><li>서명만 있는 Javadoc도 마찬가지</li></ul><h4 id="C4-성의없는-주석"><a href="#C4-성의없는-주석" class="headerlink" title="C4(성의없는 주석)"></a>C4(성의없는 주석)</h4><ul><li>작성할 가치가 있는 주석은 잘 작성할 가치가 있도록 정성껏 신중하게 선택한 단어로 작성</li><li>주절대지 않고 당연한소리 반복없이 간결, 명료하게 올바른 문법으로 작성하도록 한다</li></ul><h4 id="C5-주석-처리된-코드"><a href="#C5-주석-처리된-코드" class="headerlink" title="C5(주석 처리된 코드)"></a>C5(주석 처리된 코드)</h4><ul><li>얼마나 오래된 코드인지, 중요한 코드인지 알 수가 없음</li><li>또한 삭제도 함부로 할 수 없게 됨(다른 사람 히스토리를 모르므로)</li><li>낡아간다.나중에 다른 코드들이 변경 된후에 주석을 풀면 에러덩어리</li><li>필요하면 이전 버전을 가져오면 되므로 즉각 삭제하라!</li></ul><h3 id="환경-Environment"><a href="#환경-Environment" class="headerlink" title="환경 - Environment"></a>환경 - Environment</h3><h4 id="E1-여러-단계로-빌드"><a href="#E1-여러-단계로-빌드" class="headerlink" title="E1(여러 단계로 빌드)"></a>E1(여러 단계로 빌드)</h4><ul><li>빌드는 간단히 한단계의 명령어로 전체를 체크하웃해서 빌드 할 수 있도록</li></ul><h4 id="E2-여러-단계로-테스트"><a href="#E2-여러-단계로-테스트" class="headerlink" title="E2(여러 단계로 테스트)"></a>E2(여러 단계로 테스트)</h4><ul><li>모든 단위 테스트는 한 명령으로 돌려야 한다.</li><li>IDE의 버튼 하나, 쉘의 명령 하나 등</li></ul><h3 id="함수-Function"><a href="#함수-Function" class="headerlink" title="함수 - Function"></a>함수 - Function</h3><h4 id="F1-너무-많은-인수"><a href="#F1-너무-많은-인수" class="headerlink" title="F1(너무 많은 인수)"></a>F1(너무 많은 인수)</h4><ul><li>함수에서 인수개수는 작을수록 좋다 . 없으면 더 좋다</li><li>넷 이상은 가치가 의심스러우므로 최대한 회피</li></ul><h4 id="F2-출력인수"><a href="#F2-출력인수" class="headerlink" title="F2(출력인수)"></a>F2(출력인수)</h4><ul><li>일반적으로 읽는 이는 인수를 입력으로 간주한다.</li><li>따라서 출력인수는 일반적인 직관을 정면 위배</li><li>상태 변경이 필요하다면 출력 인수 사용 대신 함수가 속한 객체의 상태를 변경하라</li></ul><h4 id="F3-플래그-인수"><a href="#F3-플래그-인수" class="headerlink" title="F3(플래그 인수)"></a>F3(플래그 인수)</h4><ul><li>Flag Boolean은 함수가 여러 기능을 수행하는 명백한 증거</li><li>다른 함수로 쪼개고 네이밍 제대로</li></ul><h4 id="F4-죽은-함수"><a href="#F4-죽은-함수" class="headerlink" title="F4(죽은 함수)"></a>F4(죽은 함수)</h4><ul><li>아무도 호출하지 않는 함수</li><li>낭비. 삭제!</li></ul><h3 id="일반-General"><a href="#일반-General" class="headerlink" title="일반 - General"></a>일반 - General</h3><h4 id="G1-한-소스파일에-여러-언어"><a href="#G1-한-소스파일에-여러-언어" class="headerlink" title="G1(한 소스파일에 여러 언어)"></a>G1(한 소스파일에 여러 언어)</h4><ul><li>어떤 jsp : HTML, JAVA, tag Library, English comment, Javadoc, XML, JavaScript</li><li>이상적으로는 파일 하나에 언어 하나!</li><li>현실적으로 불가피하지만 최대한 줄이도록 노력 필요!</li></ul><h4 id="G2-당연한-동작을-구현하지-않는다"><a href="#G2-당연한-동작을-구현하지-않는다" class="headerlink" title="G2(당연한 동작을 구현하지 않는다)"></a>G2(당연한 동작을 구현하지 않는다)</h4><ul><li>최소 놀람의 원칙(The Principle of Least Surprise)</li><li>당연하게 여길만한 동작과 기능이 올바르게 제공되어야 한다.</li><li>그러지 않으면 신뢰할 수 없게 되고 직관적으로 예상할 수 없음</li></ul><h4 id="G3-올바로-처리되지-않은-경계"><a href="#G3-올바로-처리되지-않은-경계" class="headerlink" title="G3(올바로 처리되지 않은 경계)"></a>G3(올바로 처리되지 않은 경계)</h4><ul><li>코드는 올바로 작동 해야 →올바른 동작은 아주 복잡하다</li><li>모든 경계조건을 테스트하는 테스트 케이스를 작성하라</li></ul><h4 id="G4-안전절차-무시"><a href="#G4-안전절차-무시" class="headerlink" title="G4(안전절차 무시)"></a>G4(안전절차 무시)</h4><ul><li>UUID 직접 제어, 컴파일러 경고 끄기, 실패 테스트 나중에 미루는 태도 등</li><li>위험하다</li></ul><h4 id="G5-중복"><a href="#G5-중복" class="headerlink" title="G5 중복"></a>G5 중복</h4><ul><li>이 책의 가장 중요한 규칙중 하나</li><li>다른 이들의 언급<ul><li>DRY -  데이비드 토머스와 앤디 헌트 - 실용주의 프로그래머<ul><li>Don’t Repeat Yourself(반복하지 마라)</li><li>계방 폐쇄 원칙은 DRY 원칙이 적용되어야만 적용되는 원칙</li><li>단일 책임 원칙도 DRY에 의존한다</li></ul></li><li><a href="http://wiki.c2.com/?OnceAndOnlyOnce">ONCE, AND ONLY ONCE</a><ul><li>매 행동 각기 모두 한번만, 딱 한번만 소스에 나타나야 한다</li><li>검색에 따라선 DRY 원칙의 subset이라고 표현된 곳도 있다.</li><li>KENT BACK : XP 핵심 규칙중 하나다!</li><li>론 제프리스(XP 창시자중 하나, 최근 The Nature of Software Development을 쓴) : “모든 테스트를 통과한다”는 규칙 다음으로 중요하다</li></ul></li></ul></li><li>중복을 발견할 떄마다 추사화할 기회로 간주!<ul><li>추상화로 중복 정리 → 설계 언어 어휘 증가 → 다른이들도 어휘 사용이 쉬워짐 → 높은 추상화 → 구현이 빨라지고 오류가 적어짐</li></ul></li><li>패턴<ul><li>뻔한 패턴 : 똑같은 코드(copy&amp;paste처럼) → 간단한 함수로 교체</li><li>미묘한 패턴 : 여러 모듈에서 switch,if/else로 같은 조건 거듭 확인하는 중복 → 다형성으로 대체</li><li>더더욱 미묘한 패턴 : 유사 알고리즘인데 코드가 다른 중복 → 중복은 중복 →템플릿 메서드 패턴, 전략 패턴 사용으로 중복 제거</li></ul></li><li>최근 15년의 디자인 패턴 대다수는 중복을 잘 제거하는 방법에 불과<ul><li>BCNF: DB 스키마에서 중복 제거 전략</li><li><strong>∞</strong> : 역시 모듈 정리, 중복 제거 전략</li><li>구조적 프로그래밍도 마찬가지</li></ul></li><li>결론 : 무조건 중복을 발견하면 없애라!</li></ul><h4 id="G6-올바르지-못한-추상화-수준"><a href="#G6-올바르지-못한-추상화-수준" class="headerlink" title="G6(올바르지 못한 추상화 수준)"></a>G6(올바르지 못한 추상화 수준)</h4><ul><li>추상화 : 저차원 상세 개념에서 고차원 일반 개념을 분리한다</li><li>추상화 수행 : (고차원 일반 개념 표현의)추상 클래스와 (저차원 상세 개념)을 표현하는 파생 클래스 생성</li><li>추상화 분리는 철저히 : 모든 저차원 개념은 파생 클래스, 모든 고차원 개념은 추상 클래스<ul><li>ex) 세부구현 관련 상수, 변수, 유틸리티 : 추상은 구현 정보에 무지해야 하므로 파생에 넣어야 한다</li></ul></li><li>소스파일, 컴포넌트 모듈도 마찬가지<ul><li>좋은 SA : 개념을 다양한 차원으로 분리 → 다른 컨테이너에 넣는다</li><li>떄로는 기초 클래스 + 파생 클래스 분리, 때로는 소스파일, 모듈, 컴포넌트로 분리</li></ul></li><li>잘못된 추상화는 꼼수나 임시로 해결이 불가 : 가장 개발자에게 어려운 작업 중 하나가 추상화</li></ul><h4 id="G7-기초-클래스가-파생-클래스에-의존"><a href="#G7-기초-클래스가-파생-클래스에-의존" class="headerlink" title="G7(기초 클래스가 파생 클래스에 의존)"></a>G7(기초 클래스가 파생 클래스에 의존)</h4><ul><li>앞 G6에서 봤던 부분 다시 생각해보자. </li><li>개념을 기초+파생으로 나누는 이유<ul><li>고차원 기초 클래스 개념을 파생 클래스 개념으로부터 분리</li><li>독립성을 보장하기 위해서</li></ul></li><li>따라서 기초가 파생에 의존하면 문제가 있음</li><li>예외<ul><li>파생 클래스 개수가 <strong>확실히 고정</strong>되어있다면 기초 클래스에서 파생 클래스를 선택하는 코드가 들어간다</li><li>책의 FSM 구현 : 기초 클래스+파생 클래스 굉장히 밀접하며 언제나 같은 JAR 파일로 배포</li><li>일반적으로는 기초와 파생클래스를 다른 JAR로 배포하는 것이 좋다<ul><li>이렇게 배포시 (기초에서 파생 JAR를 전혀 모른다면) 독립적인 개별 컴포넌트 단위로 시스템 배치가 가능</li><li>컴포넌트 변경시 해당 컴포넌트만 다시 배치 가능</li><li>변경 영향이 작아서 현장에서 시스템 유지보수가 수월함</li></ul></li></ul></li></ul><h4 id="G8-과도한-정보"><a href="#G8-과도한-정보" class="headerlink" title="G8(과도한 정보)"></a>G8(과도한 정보)</h4><ul><li>높은 응집도, 낮은 결합도<ul><li>잘 정의된 모듈은 인터페이스가 아주 작고 함수가 적으며 많은 동작이 가능</li><li>부실한 모듈은 간단한 동작에 온갖 인터페이스와 꼭 호출해야하는 온갖 함수 → 높은 결합도 유발</li></ul></li><li>잘하는 개발<ul><li>클래스/모듈 인터페이스에 노출할 함수 제한</li><li>클래스가 제공 메서드 수는 작을 수록</li><li>함수가 아닌 변수 수도 작을수록</li><li>클래스의 인스턴스 변수 수도 작을수록 좋다.</li><li></li></ul></li><li>이렇게 하여라<ul><li>숨겨라: data, 유틸리티 함수, 상수, 임시 변수</li><li>피하라 : 메서드나 인스턴스 변수가 넘쳐나는 클래스는 피하라</li><li>마구 만들지 마라 : 하위에서 필요하다고 protected 변수 함수를 마구 생성하지 말아라</li><li>인터페이스를 매우 작게! 깐깐하게 만들고! 정보를 제한해서 결합도를 낮춰라</li></ul></li></ul><h4 id="G9-죽은-코드"><a href="#G9-죽은-코드" class="headerlink" title="G9(죽은 코드)"></a>G9(죽은 코드)</h4><ul><li><p>죽은 코드 : 실행되지 않는 코드</p></li><li><p>죽은 코드의 예</p><ul><li>불가능한 조건을 확인하는 if문</li><li>throw문이 없는 try문의 catch 블럭</li><li>아무도 호출하지 않는 유틸리티 함수</li><li>switch/case에서 불가능한 case 조건</li></ul></li><li><p>죽은 코드를 제거하라</p><ul><li>시간이 지날수록 악취가 심해진다(설계가 변해도 제대로 수정이 안되므로)</li></ul></li></ul><h4 id="G10-수직-분리된-경우"><a href="#G10-수직-분리된-경우" class="headerlink" title="G10(수직 분리된 경우)"></a>G10(수직 분리된 경우)</h4><blockquote><p>변수 함수는 사용되는 위치에 최대한 수직으로 분리되지 않고 가깝게 정의</p></blockquote><ul><li>지역변수: 처음으로 사용하기 직전에 선언, 수직으로 가까운 곳에 위치</li><li>비공개 함수 : 처음으로 호출한 직후 정의(클래스 scope여도 그래도 호출 부위와 가깝도록 유지)</li></ul><h4 id="G11-일관성-부족"><a href="#G11-일관성-부족" class="headerlink" title="G11(일관성 부족)"></a>G11(일관성 부족)</h4><ul><li>어떤 개념을 특정 방식으로 구현했다면 유사한 개념도 같은 방식으로 구현한다.</li><li>최소놀람의 원칙에도 부합</li><li>신중하게 선택한 표기법을 신중하게 따른다</li><li>예를들어 response변수에 HttpServletResponse 인스턴스를 저장했으면<br>HttpServletResponse를 사용하는 다른 함수에서도 일관성 있도록 동일한 변수 사용</li><li>착실히 적용하면 이리 간단한 일관성 만으로 코드가 읽고 수정이 쉬워진다.</li></ul><h4 id="G12-잡동사니"><a href="#G12-잡동사니" class="headerlink" title="G12(잡동사니)"></a>G12(잡동사니)</h4><ul><li>비어있는 기본 생성자</li><li>아무도 사용하지 않는 변수</li><li>아무도 호출하지 않는 함수</li><li>정보를 제공하지 못하는 주석</li></ul><h4 id="G13-인위적-결합"><a href="#G13-인위적-결합" class="headerlink" title="G13(인위적 결합)"></a>G13(인위적 결합)</h4><ul><li>서로 무관한 개념을 인위적으로 결합하지 않는다.</li><li>예) 일반적 enum은 특정 클래스에 속할 이유가 없다<ul><li>enum이 A클래스에 속한다면? enum을 사용하는 B 클래스가 A클래스를 알아야 한다</li><li>범용 static함수도 마찬가지로 특정 클래스에 속할 이유가 없다</li></ul></li><li>인위적 결합 : 직접 상호작용이 없는 두 모듈 사이에서 일어난다<ul><li>뚜렷한 목적없이 변수/상수/함수를 당장 편한 위치에 넣어버린 결과</li></ul></li><li>변수/상수/함수를 선언할때는 시간을 들여 올바른 위치를 고민한다</li></ul><h4 id="G14-기능-욕심"><a href="#G14-기능-욕심" class="headerlink" title="G14(기능 욕심)"></a>G14(기능 욕심)</h4><ul><li>마틴 파울러가 말하는 코드 냄새중 하나</li><li>클래스 메서드는 자기 클래스의 변수와 함수에 관심을 가져야지 다른 클래스의 변수와 함수에 가져서는 안된다.</li><li>메서드가 다른 객체의 메서드를 이용해 객체 내용 조작? 메서드가 그 객체 클래스의 범위를 욕심 내는 탓</li><li>자신이 그 클래스에 속해 그 클래스의 변수를 직접 조작 원한다는 뜻</li><li>기능 욕심은 한 클래스의 속사정을 다른 클래스에 노출하므로 별다른 문제가 없으면 제거가 좋다</li><li>어쩔 수 없는 경우 예시<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HoulyEmployeeReport</span>{</span><br><span class="line">  <span class="keyword">private</span> HourlyEmployee employee;</span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">HoulyEmployeeReport</span><span class="params">(HourlyEmployee employee)</span>{</span><br><span class="line">    <span class="built_in">this</span>.employee=employee;</span><br><span class="line">  }</span><br><span class="line">  String <span class="title function_">reportHours</span><span class="params">()</span>{</span><br><span class="line">    <span class="keyword">return</span> String.format(</span><br><span class="line">      <span class="string">"NAME: %s\tHours:%d.%1d\n"</span>,</span><br><span class="line">      employee.getName(),</span><br><span class="line">      employee.getTenthsWorked()/<span class="number">10</span>,</span><br><span class="line">      employee.getTenthsWorked()%<span class="number">10</span></span><br><span class="line">    );</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><code>HoulyEmployeeReport</code>클래스의 <code>reportHours()</code>메서드는 <code>HourlyEmployee</code>클래스를 욕심낸다</li><li>그렇다고 <code>HourlyEmployee</code>클래스가 보고서 형식을 알 필요는 없다.</li><li>만약 예외를 두지 않고 함수를 <code>HourlyEmployee</code>로 옮기면 여러 원칙 위반(특히 SRP, OCP, CCP위반)<ul><li><code>HourlyEmployee</code>가 보고서 형식과 결합 → 보고서 형식이 바뀌면 클래스가 바뀌게 되어버림</li></ul></li></ul></li></ul><h4 id="G15-선택자-인수"><a href="#G15-선택자-인수" class="headerlink" title="G15(선택자 인수)"></a>G15(선택자 인수)</h4><ul><li>인수에 따라 다른 동작 - F3(flag 인수)과도 유사한 부분이 있음</li><li>selector 인수 : 목적 기억도 어렵, 각 선택자 인수가 여러 함수를 하나로 조합</li><li>큰 함수를 쪼개지 않으려는 게으름 소산</li><li>함수 동작을 제어하려는 인수는 바람직하지 않다. 새로운 함수를 만들자.</li></ul><h4 id="G16-모호한-의도"><a href="#G16-모호한-의도" class="headerlink" title="G16(모호한 의도)"></a>G16(모호한 의도)</h4><ul><li>행을 바꾸지 않고 표현한 수식, 헝가리식 표기법, 매직 넘버 : 작성자 의도를 흐린게한다</li><li>의도를 최대한 분명히 밝힌다(시간을 투자해서라도)</li></ul><h4 id="G17-잘못-지운-책임"><a href="#G17-잘못-지운-책임" class="headerlink" title="G17(잘못 지운 책임)"></a>G17(잘못 지운 책임)</h4><ul><li>개발자가 내리는 가장 중요한 결정 : 코드 배치 위치</li><li>예) PI 상수는? 삼각함수를 선언한 클래스에 선언된다 → 독자가 자연스럽게 기대할 위치</li><li>여기서도 최소 놀람의 원칙이 적용되는 것을 알 수 있다.<br> 다른 예시</li><li>직원이 근무한 총 시간을 보고서로 출력하는 함수의 위치는 둘중 어디가 맞을까?<ol><li>보고서를 출력하는 함수에서 총계를 계산</li><li>근무 시간을 입력받는 코드에서 총계를 계산</li></ol></li><li>결정을 내리는 방법<ol><li>이 함수이름을 살펴본다 : 예로 <code>getTotalHours()</code></li><li>이름만 보았을때 어디에서 총계를 계산하는게 나은가?</li></ol></li><li>답은 1번일 것</li></ul><p>성능 때문에 2번답이 좋다고 판단된다면?</p><ul><li>이런 사실을 반영해서 네이밍 제대로 해야함</li><li>위의 경우에는 2번의 모듈에 <code>computeRunningTotalOfHours()</code>의 이름</li></ul><h4 id="G18-부적절한-Static-함수"><a href="#G18-부적절한-Static-함수" class="headerlink" title="G18(부적절한 Static 함수)"></a>G18(부적절한 Static 함수)</h4><p>좋은 static 예 : <code>Math.max(double a, double b)</code></p><ul><li>특정 인스턴스 관련 기능이 아니다</li><li>max 메서드가 사용하는 정보는 2개의 인자가 전부.</li><li>메서드 소유한 객체에서 가져오는 정보가 거의 없음</li><li>(결정적) override할 가능성이 전혀 없음</li></ul><figure class="highlight java"><figcaption><span>static으로 정의하면 안되는 함수</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HourlyPayCalculator.calculatePay(employee, overtimeRate);</span><br></pre></td></tr></tbody></table></figure><ul><li>함수를 재정의할 가능성 존재 : 수당 계산 알고리즘이 여러개 일 수도</li><li>따라서 static으로 하면 안되고 Employee클래스에 속하는 인스턴스 함수여야 한다</li><li>일반적으로 static함수보다 인스턴스 함수가 더 좋다. 조금만 의심스럽다면 인스턴스 함수로 정의</li><li>재정의 가능성을 꼭 곰곰히 따져보고 static 정의 고려할 것</li></ul><h4 id="G19-서술적-변수"><a href="#G19-서술적-변수" class="headerlink" title="G19(서술적 변수)"></a>G19(서술적 변수)</h4><ul><li><a href="https://en.wikipedia.org/wiki/Kent_Beck">kent beck</a>이 다음의 훌륭한 책에서 지적<ul><li><a href="https://www.google.com/search?q=smalltalk+best+practice+patterns&amp;rlz=1C1GCEA_enKR963KR964&amp;oq=Smalltalk+Best+Practice+Patterns&amp;aqs=chrome.0.0i355i512j46i512j0i512l2j0i22i30.345j0j9&amp;sourceid=chrome&amp;ie=UTF-8">Smalltalk Best Practice Patterns, 1996</a></li><li><a href="http://www.yes24.com/Product/Goods/2824034">Implementation Patterns(켄트백의 구현 패턴), 2007</a></li></ul></li><li>프로그램 가독성을 가장 높이는 효과적인 방법 : <strong>계산을 여러 단계로 나누고 중간값으로 서술적 변수 이름 사용</strong><figure class="highlight java"><figcaption><span>FitNesse 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Matcher</span> <span class="variable">match</span> <span class="operator">=</span> headerPattern.matcher(line);</span><br><span class="line"><span class="keyword">if</span>(match.find()){</span><br><span class="line">  <span class="type">String</span> <span class="variable">key</span> <span class="operator">=</span> match.group(<span class="number">1</span>);</span><br><span class="line">  <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> match.group(<span class="number">2</span>);</span><br><span class="line">  headers.put(key.toLowerCase(), value);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>서술적 이름 사용 때문에 첫번째 그룹이 key, 2번째 그룹이 value라는 사실이 들어난다</li><li>서술적이름은 많이 써도 괜찮다. 일반적으로는 많을 수록 더 좋다</li><li>계산을 몇 단계로 나누고 중간값에 좋은 변수 이름만 부텽도 해독하기 어렵던 모듈이<br>순식간에 읽기 좋은 모듈로 바뀐다</li></ul><h4 id="G20-이름과-기능이-일치하는-함수"><a href="#G20-이름과-기능이-일치하는-함수" class="headerlink" title="G20(이름과 기능이 일치하는 함수)"></a>G20(이름과 기능이 일치하는 함수)</h4><ul><li><code>date.add(5);</code> → 5일? 5주? 5시간? date인스턴스 변경인가, 아니면 새로운 Date를 리턴하는 함수인가?</li><li>코드를 봐서 알 수가 없음</li><li>기존 인스턴스 변경 : <code>addDaysTo</code>, <code>increaseByDays</code> 등의 이름이 좋다</li><li>새로운 객체 반환 : <code>daysLater</code>, <code>daysSince</code>등의 이름이 좋다</li><li>이름으로 분명치 않다면? 좋은 이름으로 바꾸거나, 좋은 이름을 붙이기 쉽게 기능을 정리하거나</li></ul><h4 id="G21-알고리즘을-이해하라"><a href="#G21-알고리즘을-이해하라" class="headerlink" title="G21(알고리즘을 이해하라)"></a>G21(알고리즘을 이해하라)</h4><ul><li>알고리즘의 충분한 이해없이 여기저기 if와 flag를 삽입하며 돌려서 결과를 만드는데 성공한 코드들</li><li>테스트를 모두 통과한다는 것만으로 부족 → 알고리즘이 올바르다는 사실을 알아야 한다</li><li>알고리즘의 올바름을 확인/이해하려면? 기능이 뻔히 보일정도로 깔끔하고 명확하게 함수를 재구성 하는 것이 최고의 방법</li></ul><h4 id="G22-논리적-의존성은-물리적으로-드러내라"><a href="#G22-논리적-의존성은-물리적으로-드러내라" class="headerlink" title="G22(논리적 의존성은 물리적으로 드러내라)"></a>G22(논리적 의존성은 물리적으로 드러내라)</h4><ul><li>모듈이 다른 모듈에 의존한다면 논리적 의존성으로는 부족하며 물리적인 의존성도 있어야 한다<br>책의 예시</li><li><code>HourlyReporter</code> 클래스의 <code>generateReport()</code>는 <code>PAGE_SIZE</code>라는 상수를 이용해 돌아간다</li><li>논리적 의존성 :  55의 값으로 선언된 <code>PAGE_SIZE</code>라는 상수 →</li><li>페이지 크기는 <code>HourlyReporter</code>가 아닌 <code>HourlyReportFormatter</code>가 책임질 정보</li><li>앞서 설명한 [G17]에 해당한 실수</li><li>논리적 의존성 : <code>HourlyReporter</code>클래스는 <code>HourlyReportFormatter</code>가 55 페이지 크기를 처리할 줄 안다는 사실에 의존 → 이 페이지 크기를 알거라고 가정한 자체가 논리적 의존성</li><li>가정이 틀리면 오류가 되어버림</li><li>해결책 : 논리적 의존성을 물리적 의존성으로<ul><li><code>HourlyReportFormatter</code>에 <code>getMaxPageSize()</code>메서드를 추가</li><li><code>HourlyReporter</code>클래스는 <code>PAGE_SIZE</code> 상수 대신 <code>getMaxPageSize()</code>호출</li></ul></li></ul><h4 id="G23-if-x2F-else-Switch-x2F-Case-대신-다형성-사용"><a href="#G23-if-x2F-else-Switch-x2F-Case-대신-다형성-사용" class="headerlink" title="G23 if/else, Switch/Case 대신 다형성 사용"></a>G23 if/else, Switch/Case 대신 다형성 사용</h4><ul><li>3장에서 밥 아저씨 : “새 유형보다 새 함수를 추가할 확율이 높은 코드는 switch가 더 적합하다”<ol><li>대다수 개발자 swit문 선택 이유 : 올바른 선택이 아니라 손쉬운 선택이기 때문<br>그러므로 Switch 선택 전에 다형성 먼저 고려하라는 의</li><li>유형보다 함수가 더 쉽게 변하는 경우는 극히 드물다 → 그러므로 모든 switch문을 의심하라</li></ol></li><li>밥아저씨가 따르는 ‘one switch” 규칙<ul><li>선택 유형 하나에는 switch문을 한 번만 사용</li><li>같은 선택을 수행하는 다른 코드에는 다형성 객체를 생성해 switch문을 대신한다</li></ul></li></ul><h4 id="G24-표준-표기법"><a href="#G24-표준-표기법" class="headerlink" title="G24(표준 표기법)"></a>G24(표준 표기법)</h4><ul><li>팀은 업계 표준에 기반한 구현 표준을 따라야 한다</li><li>팀이 정한 표준은 팀원들 모두가 따라야 한다</li><li>밥아저씨가 따르는 표기법 : p512 목록 B-7 ~ B-14</li></ul><h4 id="G25-매직-숫자는-명명된-상수로-교체"><a href="#G25-매직-숫자는-명명된-상수로-교체" class="headerlink" title="G25(매직 숫자는 명명된 상수로 교체)"></a>G25(매직 숫자는 명명된 상수로 교체)</h4><ul><li>SW개발 에서 가장 오래된 규칙 : 일반적으로 코드에서 숫자를 사용하지 말라는 규칙</li><li>상수가 너무 미해가 쉽고 자명하다면 숨길 필요는 없음: 하루는 24시간, 1분은 60초 등</li><li>원주율: 자명하지만 숫자가 길고 근사값 사용시 오류 발생 : 다행히 Math.PI를 사용하면 된다</li><li>매직숫자는 단순히 숫자만 의미하느넥 아니라 의미가 분명하지 않은 토큰 모두를 말한다</li></ul><h4 id="G26-정확하라"><a href="#G26-정확하라" class="headerlink" title="G26(정확하라)"></a>G26(정확하라)</h4><p>정확하지 않은 행동</p><ul><li>검색결과 중 첫 번째 결과만 유일한 결과로 간주 → 순진</li><li>부동 소수점으로 통화 표현 → 범죄</li><li>갱신할 가능성이 희박하다고 잠금/트랜잭션 관리를 건더뛰는 것 → 게으름</li><li>List로 선언할 변수를 ArrayList로 선언 → 지나친 제약</li><li>모든 변수를 protected로 선언 → 무절제</li><li>코드에서 무언가를 결정할때는 정확하게 결정한다<ul><li>결정을 내리는 이유와 예외를 처리할 방법을 분명히 알아야 한다. 대충하면 안된다.</li><li>호출하는 함수가 null 반환할지 모른다 → 반드시 null 점검</li><li>검색 결과가 하나뿐이라고 짐작 → 하나인지 확실히 확인</li><li>통화를 다룬다면 정수를 사용하고 반올림을 올바로 처리한다(아니면 Money클래스 사용)</li><li>concurrent 특성으로 동시 갱신 가능성 존재? → 적절한 잠금 매커니즘</li></ul></li></ul><h4 id="G27-관례보다-구조-사용"><a href="#G27-관례보다-구조-사용" class="headerlink" title="G27(관례보다 구조 사용)"></a>G27(관례보다 구조 사용)</h4><ul><li>설계 결정 강제시 규칙보다 관례를 사용<br>예시</li><li>Enum변수가 멋진 Switch/case &lt;&lt;&lt;&lt; 추상 메서드가 있는 기초 클래스</li><li>Switch/case를 매번 똑같이 구현하게 강제하기는 어렵다</li><li>파생 클래스는 추상 메서드를 모두 구현하지 않으면 안되므로</li></ul><h4 id="G28-조건을-캡슐화"><a href="#G28-조건을-캡슐화" class="headerlink" title="G28(조건을 캡슐화)"></a>G28(조건을 캡슐화)</h4><ul><li>부울논리는 (if,while에 넣어 생각하지 않아도) 이해가 오래걸림</li><li>조건의 의도를 분명히 밝히는 함수</li><li>예를 들어 <code>if(timer.hasExpired() &amp;&amp; !timer.isRecurrent())</code>보다는<br><code>if (shouldBeDeleted(timer))</code>라는 코드가 좋다</li></ul><h4 id="G29-부정조건은-피하라"><a href="#G29-부정조건은-피하라" class="headerlink" title="G29(부정조건은 피하라)"></a>G29(부정조건은 피하라)</h4><ul><li>부정 조건은 긍정보다 이해를 어렵게 하므로 가능한 긍정 조건으로 표현</li></ul><h4 id="G30-함수는-한-가지만"><a href="#G30-함수는-한-가지만" class="headerlink" title="G30(함수는 한 가지만)"></a>G30(함수는 한 가지만)</h4><ul><li><p>함수는 단일 입무만.</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//밑의 함수는 한 가지만 수행하는 함수가 아니다.</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pay</span><span class="params">()</span> {</span><br><span class="line">  <span class="keyword">for</span> (Employee e : employees) {</span><br><span class="line">    <span class="keyword">if</span> (e.isPayday()){</span><br><span class="line">      <span class="type">Money</span> <span class="variable">pay</span> <span class="operator">=</span> e. calculatePay();</span><br><span class="line">      e.deliverPay(pay);</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p>여러 단락을 하는 함수는 작은 함수 여럿으로 나눠야 마땅하다</p><figure class="highlight java"><figcaption><span>이제 각 함수는 한가지 임무만 수행</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pay</span><span class="params">()</span> {</span><br><span class="line">  <span class="keyword">for</span> (Employee e : employees) {</span><br><span class="line">    payIfNecessary(e);</span><br><span class="line">  }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">payIfNecessary</span><span class="params">(Employee e)</span> {</span><br><span class="line">  <span class="keyword">if</span> (e.isPayday())</span><br><span class="line">    calculateAndDeliveryPay(e);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="title function_">calculateAndDeliveryPay</span><span class="params">(Employee e)</span> {</span><br><span class="line">  <span class="type">Money</span> <span class="variable">pay</span> <span class="operator">=</span> e. calculatePay();</span><br><span class="line">  e.deliverPay(pay);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="G31-숨겨진-시간적-결합"><a href="#G31-숨겨진-시간적-결합" class="headerlink" title="G31(숨겨진 시간적 결합)"></a>G31(숨겨진 시간적 결합)</h4><ul><li>때론 시간적 결합이 필요하지만 이를 숨겨서는 안된다</li><li>함수를 짤 때는 함수 인수를 적절히 배치해 함수 호출 순서를 명백히 드러내야 한다<figure class="highlight java"><figcaption><span>시간적 결합이 필요한 함수</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MoogDiver</span> {</span><br><span class="line">  Gradient gradient;</span><br><span class="line">  List&lt;Spline&gt; splines;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dive</span><span class="params">(String reason)</span> {</span><br><span class="line">    <span class="comment">//밑의 3가지 함수는 실행되는 순서가 중요하다. 하지만 시간적 결합을 강제하지 않는다.</span></span><br><span class="line">    saturateGradient();</span><br><span class="line">    reticulateSplines();</span><br><span class="line">    diveForMoog(reason);</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><figure class="highlight java"><figcaption><span>더 좋은 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MoogDiver</span> {</span><br><span class="line">  Gradient gradient;</span><br><span class="line">  List&lt;Spline&gt; splines;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dive</span><span class="params">(String reason)</span> {</span><br><span class="line">    <span class="type">Gradient</span> <span class="variable">gradient</span> <span class="operator">=</span> saturateGradient();</span><br><span class="line">    List&lt;Spline&gt; splines = reticulateSplines(gradient);</span><br><span class="line">    diveForMoog(splines, reason);</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>설명</p><ol><li>강제로 인자를 연결 소자로 만들어 시간적 결합을 노출</li></ol><ul><li>각 함수의 결과는 다음 함수에 필요하므로 순서를 바꿔서 호출할 수가 없다</li></ul><ol start="2"><li>함수가 복잡해졌다</li></ol><ul><li>의도적으로 추가한 구문적인 복잡성이 원래 있던 시간의 복잡성을 드러낸 것이다.</li></ul><ol start="3"><li>인스턴스 변수를 그대로 두었다는 사실에 주목</li></ol><ul><li>해당 클래스의 private메서드에 필요한 변수일지 모른다</li><li>그렇다 하더라도 제자리를 찾은 변수들이 시간적 결합을 좀 더 명백히 드러나게 해줄 것이다</li></ul><h4 id="G32-일관성을-유지하라"><a href="#G32-일관성을-유지하라" class="headerlink" title="G32(일관성을 유지하라)"></a>G32(일관성을 유지하라)</h4><ul><li>코드 구조를 잡을 떄는 이유를 고민하고 이유를 코드 구조를 명백히 표현하라</li><li>일관성 없는 구조 → 남들이 마음대로 바꿔도 괜찮다고 생각한다.</li><li>시스템 전반에 걸쳐 일관성 있는 구조 → 남들도 따르고 보존</li></ul><h4 id="G33-경계-조건을-캡슐화"><a href="#G33-경계-조건을-캡슐화" class="headerlink" title="G33(경계 조건을 캡슐화)"></a>G33(경계 조건을 캡슐화)</h4><ul><li>경계 조건은 빼먹거나 놓치기 쉬우므로 여기저기에서 처리하지 않고 한 곳에서 별도로 처리한다</li><li>+1, -1을 흩어 놓지 말자<figure class="highlight java"><figcaption><span>FIT에서 가져온 예제</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(level + <span class="number">1</span> &lt; tags.length&gt;) {</span><br><span class="line">  parts = <span class="keyword">new</span> <span class="title class_">Parse</span>(body, tags, level + <span class="number">1</span>, offset + endTag);</span><br><span class="line">  body = <span class="literal">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>level + 1이 2번 나온다. 이런 경계 조건은 변수로 처리하자<figure class="highlight java"><figcaption><span>경계조건 캡슐화</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">nextLevel</span> <span class="operator">=</span> level + <span class="number">1</span>;</span><br><span class="line"><span class="keyword">if</span>(nextLevel &lt; tags.length&gt;) {</span><br><span class="line">  parts = <span class="keyword">new</span> <span class="title class_">Parse</span>(body, tags, nextLevel, offset + endTag);</span><br><span class="line">  body = <span class="literal">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="G34-함수는-추상화-수준을-한-단계만-내려가야-한다"><a href="#G34-함수는-추상화-수준을-한-단계만-내려가야-한다" class="headerlink" title="G34(함수는 추상화 수준을 한 단계만 내려가야 한다)"></a>G34(함수는 추상화 수준을 한 단계만 내려가야 한다)</h4><ul><li>함수 내 추상화 수준은 함수 이름이 의미하는 작업보다 한 단계만 낮아야 한다</li><li>함수 내 모든 문장은 추상화 수준이 동일해야 한다</li><li>이번 장에서 가장 이해하기 어렵고 따르기도 어려운 항목</li><li>개념은 간단하지만 인간은 추상화 수준을 뒤섞는 능력이 너무 뛰어나다<figure class="highlight java"><figcaption><span>FitNess의 모듈 HruleWidget에서 가져온 Code</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">render</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  <span class="type">StringBuffer</span> <span class="variable">html</span> <span class="operator">=</span> StringBuffer(<span class="string">"&lt;hr"</span>);</span><br><span class="line">  <span class="keyword">if</span> (size &gt; <span class="number">0</span>)</span><br><span class="line">    html.append(<span class="string">" size=\""</span>).append(size+<span class="number">1</span>).append(<span class="string">"\""</span>);</span><br><span class="line">  html.append(<span class="string">"&gt;"</span>);</span><br><span class="line">  <span class="keyword">return</span> html.toString();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>내용</li><li>페이지를 가로지르는 수평자 html 태그 생성, 높이는 size변수로 지정하고 있다.</li><li><code>----</code>처럼 4개 이상의 연이은 대시를 감지해 태그로 변환하는 코드임</li><li>추상화 수준이 최소한 2개가 섞여 있다.<ol><li>수평선에 크기가 있다는 개념</li><li>HR 태그 자체의 문법</li></ol></li><li>수정 내용은 밑과 같다 → size 변수 네이밍 적용<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">render</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  <span class="type">HtmlTag</span> <span class="variable">hr</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HtmlTag</span>(<span class="string">"hr"</span>);</span><br><span class="line">  <span class="keyword">if</span> (extraDashes &gt; <span class="number">0</span>)</span><br><span class="line">    hr.addAttribute(<span class="string">"size"</span>, hrSize(extraDashes));</span><br><span class="line">  <span class="keyword">return</span> hr.html();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> String <span class="title function_">hrSize</span><span class="params">(<span class="type">int</span> height)</span> {</span><br><span class="line">  <span class="type">int</span> hrSize= height + <span class="number">1</span>;</span><br><span class="line">  <span class="keyword">return</span> String.format(<span class="string">"%d"</span>, hrSize);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>render 함수는 HR 태그만 생성한다. 태그 문법은 상관하지 않음</li><li>html 문법은 HtmlTag 모듈이 알아서 처리</li><li>HtmlTag 모듈은 xhtml 표준을 준수해서 <code>&lt;hr/&gt;</code>출력 - 미묘한 버그 캐치</li><li><strong>추상화 수준 분리는 리팩터링 수행의 가장 중요한 이유중 하나이며 제대로 하기 가장 어려운 작업</strong></li></ul><h4 id="G35-설정-정보는-최상위-단계"><a href="#G35-설정-정보는-최상위-단계" class="headerlink" title="G35(설정 정보는 최상위 단계)"></a>G35(설정 정보는 최상위 단계)</h4><ul><li>추상화 최상위에 있어야할 기본값 상수, 설정 관련 상수를 저차원 함수에 숨겨선 안된다.</li><li>대신 고차원 함수에서 저차원 함수를 호출할 때 인수로 넘긴다.</li></ul><h4 id="G36-추이적-탐색을-피하라"><a href="#G36-추이적-탐색을-피하라" class="headerlink" title="G36(추이적 탐색을 피하라)"></a>G36(추이적 탐색을 피하라)</h4><p>디미터의 법칙 :모듈은 주변 모듈을 모를 수록 좋다. 자신이 직접 사용하는 모듈만 알아야 한다</p><ul><li>A→B→C 여도 A가 C를 알 필요는 없다</li><li>따라서 <code>a.getB().getC().doSomething();</code>은 바람직하지 않다.<ul><li>여러 모듈에서 위와 같은 코드의 형태를 사용한다면?</li><li>설계와 아키텍처를 바꿔서 B와 C사이에 Q를 넣기 쉽지 않다</li><li><code>a.getB().getC()</code>를 전부 찾아 <code>a.getB().getQ().getC()</code>로 바꿔야 하기 때문</li><li>너무 많은 모듈이 아키텍처를 너무 많이 알게 됨 → 아키텍처가 굳어진다</li></ul></li><li>디미터의 법칙은 결합도와 관련된 것이며, 객체의 내부 구조가 외부로 노출되는것에 대한 것이다</li><li>따라서 아래는 예외<ul><li>Stream API등의 여러 도트는 해당사항이 없다(동일한 스트림 변환이고 캡슐화 그대로 유지)</li><li>DTO, 컬렉션 객체등 자료 구조 : 당연히 내부를 노출해야 함</li></ul></li><li>결론 : 내가 사용하는 모듈이 내게 필요한 모든 서비스를 제공해야 하며, 객체 그래프로 탐색할 필요가 없어야 한다</li></ul><h3 id="자바-JAVA"><a href="#자바-JAVA" class="headerlink" title="자바 - JAVA"></a>자바 - JAVA</h3><h4 id="J1-긴-import-대신-wild-카드-사용"><a href="#J1-긴-import-대신-wild-카드-사용" class="headerlink" title="J1(긴 import 대신 wild 카드 사용)"></a>J1(긴 import 대신 wild 카드 사용)</h4><p>의존성 문제</p><ul><li>명시적으로 클래스를 import하면 그 클래스가 반드시 존재해야 한다</li><li>와일드 카드로 패키지 지정시 특정 클래스 존재 필요 없음</li><li>import는 단순히 검색 경로에 추가하므로 진정한 의존성이 생기지 않음 → 모듈간의 결합성이 낮아진다.</li></ul><p>와일드 카드 사용 단점 : 때때로 이름 충돌, 모호성 초래</p><ul><li>이름이 같거나, 패키지가 다른 클래스는 명시적 import 사용하자</li><li>번거롭지만 자주 발생하지 않음 - 여전히 와일드카드 import &gt;&gt;명시적 import</li></ul><h4 id="J2-상수는-상속하지-않는다"><a href="#J2-상수는-상속하지-않는다" class="headerlink" title="J2(상수는 상속하지 않는다)"></a>J2(상수는 상속하지 않는다)</h4><ul><li>하위에서 발견하면 계속 위로 거슬려서 찾아봐야한다. → 언어의 범우 규칙을 속이는 행위</li><li>대신 static import를 사용하라</li></ul><h4 id="상수-대-Enum"><a href="#상수-대-Enum" class="headerlink" title="상수 대 Enum"></a>상수 대 Enum</h4><ul><li><code>public static final int</code>말고 ENUM을 사용하라!</li><li>enum은 메서드와 필드도 사용할 수 있는 강력한 도구다!</li></ul><h3 id="이름-Name"><a href="#이름-Name" class="headerlink" title="이름 - Name"></a>이름 - Name</h3><h4 id="N1-서술적인-이름-사용"><a href="#N1-서술적인-이름-사용" class="headerlink" title="N1(서술적인 이름 사용)"></a>N1(서술적인 이름 사용)</h4><ul><li>이름은 성급하지 않고 신중하게!</li><li>SW 가독성의 90%는 이름이 결정 → 시간을 들여 현명한 이름을 선택하고 적합성 유지</li></ul><h4 id="N2-적절한-추상화-수준에서-이름-선택"><a href="#N2-적절한-추상화-수준에서-이름-선택" class="headerlink" title="N2(적절한 추상화 수준에서 이름 선택)"></a>N2(적절한 추상화 수준에서 이름 선택)</h4><ul><li>구현을 드러내는 이름은 피하고 해당 클래스/함수가 위치하는 추상화 수준을 반영하는 이름을 선택하라</li><li>쉽지 않겠지만 안정적인 코드를 만들려면 지속적인 개선/노력이 필요하다<br>예시<figure class="highlight java"><figcaption><span>추상화 수준을 살펴보자</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Modem</span> {</span><br><span class="line">  <span class="type">boolean</span> <span class="title function_">dial</span><span class="params">(String phoneNumber)</span>;</span><br><span class="line">  <span class="type">boolean</span> <span class="title function_">disconnect</span><span class="params">()</span>;</span><br><span class="line">  <span class="type">boolean</span> <span class="title function_">send</span><span class="params">(<span class="type">char</span> c)</span>;</span><br><span class="line">  <span class="type">char</span> <span class="title function_">recv</span><span class="params">()</span>;</span><br><span class="line">  String <span class="title function_">getConnectedPhoneNumber</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>얼핏 보면? 적절해 보이며, 대다수 애플리케이션엔 문제가 없음</li><li>현대의 모뎀은 전용선, 케이블, USB등을 사용하기도 → 전화번호 개념은 추상화 수준이 틀렸다<figure class="highlight java"><figcaption><span>더 좋은 이름 전략</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Modem</span> {</span><br><span class="line">  <span class="type">boolean</span> <span class="title function_">connect</span><span class="params">(String connectionLocator)</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="N3-가능하다면-표준-명명법"><a href="#N3-가능하다면-표준-명명법" class="headerlink" title="N3(가능하다면 표준 명명법)"></a>N3(가능하다면 표준 명명법)</h4><p>기존 명명법 사용 이름은 이해가 쉽다</p><ul><li>Decorator 패턴 활용한다면 장식 클래스 이름에 Decorator라는 단어를 사용해야 한다.</li><li><code>AutoHangupModemDecorator</code>: 세션 끝 무렵 자동으로 연결을 끊는 기능으로 Modem을 장식하는 클래스 이름에 적합</li><li>자바에서 객체를 문자열로 변환하는 함쉬: <code>toString()</code> 많이 씀, 가급적 관례를 따르는 편이 좋다.</li><li>팀이 특정 프로젝트에 적용할 표준 - 유비쿼터스 언어(라고 에릭 에반스는 부른다)<ul><li>코드에는 이 언어에 속하는 용어를 열심히 사용하자</li><li>프로젝트에 유효한 의미가 담긴 이름을 많이 사용할 수록 독자가 코드 이해가 쉽다</li></ul></li></ul><h4 id="N4-명확한-이름"><a href="#N4-명확한-이름" class="headerlink" title="N4(명확한 이름)"></a>N4(명확한 이름)</h4><ul><li>함수나 변수의 목적을 명확히 밝히는 이름을 선택한다<figure class="highlight java"><figcaption><span>역시 FitNess 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> String <span class="title function_">doRename</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  <span class="keyword">if</span> (refactorReferences)</span><br><span class="line">    renameReferences();</span><br><span class="line">  renamePage();</span><br><span class="line"></span><br><span class="line">  pathToRename.removeNameFromEnd();</span><br><span class="line">  pathToRename.addNameToEnd(newName);</span><br><span class="line">  <span class="keyword">return</span> PathParser.render(pathToRename);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>네이밍으로 함수의 목적 파악이 분명하지 않고 광범위, 모호</li><li>doRename()안에 renamePage()함수가 들어있는데.. 이름만으로 차이점을 전혀 알 수가 없다</li><li>좋은 이름 : <code>renamePageAndOptionallyAllReferences</code><ul><li>아주 길지만 모듈에서 한번만 호출된다</li><li>길다는 단점을 서술성이 충분히 메꿔준다</li></ul></li></ul><h4 id="N5-긴-범위는-긴-이름을-사용하라"><a href="#N5-긴-범위는-긴-이름을-사용하라" class="headerlink" title="N5(긴 범위는 긴 이름을 사용하라)"></a>N5(긴 범위는 긴 이름을 사용하라)</h4><ul><li>이름 길이는 범위 길이에 비례해야 한다 : 범위가 작으면 짧은 이름, 범위가 길면 긴 이름</li><li>5줄 안팎이라면 i,j등의 변수 이름도 괜찮다<figure class="highlight java"><figcaption><span>볼링 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">rollMany</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> pins)</span> {</span><br><span class="line">  <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n ; i++)</span><br><span class="line">    g.roll(pins);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>깔끔, 오히려 i를 <code>rollCount</code>로 쓰면 더 햇갈릴 수 있다.</li><li>범위가 길수록 이름을 정확하고 길게 짓는다</li></ul><h4 id="N6-인코딩을-피하라"><a href="#N6-인코딩을-피하라" class="headerlink" title="N6(인코딩을 피하라)"></a>N6(인코딩을 피하라)</h4><ul><li>헝가리안 표기법 절대 쓰지마라</li><li>현대 개발 환경에서는 m_, f등의 접두어 불필요</li><li>IDE의 발전으로 이름 조작없이 모든 정보 파악 가능</li></ul><h4 id="N7-이름으로-부수효과를-설명하라"><a href="#N7-이름으로-부수효과를-설명하라" class="headerlink" title="N7(이름으로 부수효과를 설명하라)"></a>N7(이름으로 부수효과를 설명하라)</h4><ul><li>함수/변수/클래스가 하는 일을 모두 기술하는 이름을 사용</li><li>이름에 부수효과를 숨기지 않는다.여러 작업을 하면 해당 작업 모두를 반영하는 네이밍이 필요<figure class="highlight java"><figcaption><span>TestNG의 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> ObjectOutputStream <span class="title function_">getOos</span><span class="params">()</span> <span class="keyword">throws</span> IOException {</span><br><span class="line">  <span class="keyword">if</span> (m_oos == <span class="literal">null</span>) {</span><br><span class="line">      m_oos = <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(m_socket.getOutputStream());</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">return</span> m_oos;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>위 함수는 단순히 “oos”만 가져오지 않고, 기존에 “oos”가 없으면 생성한다.</li><li>그러므로 <code>createOrReturnOos()</code>라는 이름이 더 좋다</li></ul><h3 id="테스트-Test"><a href="#테스트-Test" class="headerlink" title="테스트 - Test"></a>테스트 - Test</h3><h4 id="T1-불충분한-테스트"><a href="#T1-불충분한-테스트" class="headerlink" title="T1(불충분한 테스트)"></a>T1(불충분한 테스트)</h4><ul><li>깨질만한 부분을 모두 테스트해야 한다</li><li>테스트 케이스가 확인하지 않는 조건이나 검증하지 않는 계산이 있다면 불완전한 테스트</li></ul><h4 id="T2-커버리지-도구-사용"><a href="#T2-커버리지-도구-사용" class="headerlink" title="T2(커버리지 도구 사용)"></a>T2(커버리지 도구 사용)</h4><ul><li>테스트가 불충분한 모듈 찾기가 쉬어진다. 붉은 색상으로 드러나는 IDE가 그러함</li></ul><h4 id="T3-사소한-테스트를-건더-뛰지-마라"><a href="#T3-사소한-테스트를-건더-뛰지-마라" class="headerlink" title="T3(사소한 테스트를 건더 뛰지 마라)"></a>T3(사소한 테스트를 건더 뛰지 마라)</h4><ul><li>사소한 테스트는 짜기 쉽다</li><li>사소한 테스트가 제공하는 문서적 가치 &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 구현에 드는 비용</li></ul><h4 id="T4-무시한-테스트는-모호함을-뜻함"><a href="#T4-무시한-테스트는-모호함을-뜻함" class="headerlink" title="T4(무시한 테스트는 모호함을 뜻함)"></a>T4(무시한 테스트는 모호함을 뜻함)</h4><ul><li>불분명한 요구사항은 테스트케이스를 <code>@Ignore</code>처리(컴파일 불가능한 상태라면 주석 처리)</li></ul><h4 id="T5-경계-조건-테스트하라"><a href="#T5-경계-조건-테스트하라" class="headerlink" title="T5(경계 조건 테스트하라)"></a>T5(경계 조건 테스트하라)</h4><ul><li>경계조건은 각별히 신경써서 테스트</li><li>알고리즘 조건은 올바로 짜놓고 경계 조건에서 실수하는 경우가 흔하다</li></ul><h4 id="T6-버그-주변은-철저히-테스트"><a href="#T6-버그-주변은-철저히-테스트" class="headerlink" title="T6(버그 주변은 철저히 테스트)"></a>T6(버그 주변은 철저히 테스트)</h4><ul><li>버그는 서로 모이는 경향</li><li>버그를 발견했다면 그 함수를 철저히 테스트하라 →</li><li>십중 팔구 다른 버그도 발견한다</li></ul><h4 id="T7-실패-패턴을-살펴라"><a href="#T7-실패-패턴을-살펴라" class="headerlink" title="T7(실패 패턴을 살펴라)"></a>T7(실패 패턴을 살펴라)</h4><ul><li>때로는 테스트 케이스가 실패하는 패턴으로 문제 진단</li><li>테스트 케이스를 최대한 꼼꼼히 짜라는 이유도 여기에 있음</li><li>예) 입력이 5자를 넘기는 케이스가 모두 실패, 특정인자로 음수를 넘어가는 케이스가 모두 실패~</li><li>테스트 리포트 빨간색/녹색만 보고도 꺠달음이 온다</li></ul><h4 id="T8-테스트-커버리지-패턴을-살펴라"><a href="#T8-테스트-커버리지-패턴을-살펴라" class="headerlink" title="T8(테스트 커버리지 패턴을 살펴라)"></a>T8(테스트 커버리지 패턴을 살펴라)</h4><ul><li>통과하는 테스트가 실행하거나 실행하지 않는 코드를 살펴라</li><li>그러면 실패하는 테스트 케이스의 원인이 들어난다</li></ul><h4 id="T9-테스트는-빨라야-한다"><a href="#T9-테스트는-빨라야-한다" class="headerlink" title="T9(테스트는 빨라야 한다)"></a>T9(테스트는 빨라야 한다)</h4><ul><li>느린 테스트는 실행하지 않게 된다</li></ul><h4 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h4><ul><li>이 리스트가 완전한 것은 아니다. 가치 체계를 피력할 뿐</li><li>가치 체계 자체가 클린 코드 책의 주제이자 목표</li><li>특정 규칙만 따른다고, 리스트를 익힌다고 클린코드가 얻어지거나 소프트웨어 장인이 되지 않는다</li><li>전문가, 장인 정신은 가치에서 나오며 그 가치에 기반한 규율과 절제가 필요</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch17/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] XVI. SerialDate 리팩터링</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch16/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch16/</guid>
      <pubDate>Wed, 09 Feb 2022 00:57:08 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;XVI-SerialDate-리팩터링&quot;&gt;&lt;a href=&quot;#XVI-SerialDate-리팩터링&quot; class=&quot;headerlink&quot; </description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="XVI-SerialDate-리팩터링"><a href="#XVI-SerialDate-리팩터링" class="headerlink" title="XVI. SerialDate 리팩터링"></a>XVI. SerialDate 리팩터링</h1><p>SerialDate</p><ul><li>JCommon 라이브러리의 <code>org.jfree.date</code>라는 패키지 이하</li><li><a href="https://github.com/jfree/jcommon/blob/master/src/main/java/org/jfree/date/SerialDate.java">https://github.com/jfree/jcommon/blob/master/src/main/java/org/jfree/date/SerialDate.java</a></li><li><a href="https://github.com/jfree/jcommon/pull/6">https://github.com/jfree/jcommon/pull/6</a><ul><li>아예 대놓고 클린코드 내용대로 리팩토링후 풀리퀘를 보낸 내용</li></ul></li></ul><hr><ul><li>Fork 저장소 : <a href="https://github.com/rkaehdaos/jcommon">https://github.com/rkaehdaos/jcommon</a></li><li>Fork 실제 작업 프로젝트 : <a href="https://github.com/rkaehdaos/jcommon/projects/2">https://github.com/rkaehdaos/jcommon/projects/2</a></li></ul><h2 id="첫째-돌려보자"><a href="#첫째-돌려보자" class="headerlink" title="첫째, 돌려보자"></a>첫째, 돌려보자</h2><ul><li><a href="https://github.com/rkaehdaos/jcommon/issues/2">https://github.com/rkaehdaos/jcommon/issues/2</a></li><li>테스트가 이렇게 중요하다.</li></ul><h2 id="둘째-고쳐보자"><a href="#둘째-고쳐보자" class="headerlink" title="둘째, 고쳐보자"></a>둘째, 고쳐보자</h2><ul><li><a href="https://github.com/rkaehdaos/jcommon/issues/5">https://github.com/rkaehdaos/jcommon/issues/5</a></li><li>변경 이력 삭제</li><li>전략패턴+팩토리 메서드 패턴으로 다형성으로 커버 안되는 부분 처리부분</li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li>테스트 내용에서 일단 충격</li><li>개인적으로는 되는 부분은 하나만 테스트하고 경계만 테스트했었는데..</li><li>반성 반성</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch16/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] XV. JUnit 들여다보기</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch15/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch15/</guid>
      <pubDate>Wed, 09 Feb 2022 00:56:49 GMT</pubDate>
      
        
        
      <description>&lt;h1 id=&quot;XV-JUnit-들여다보기&quot;&gt;&lt;a href=&quot;#XV-JUnit-들여다보기&quot; class=&quot;headerlink&quot; title=&quot;XV. JUnit 들여다보기&quot;&gt;&lt;/a&gt;XV. JUnit 들여다보기&lt;/h1&gt;&lt;p&gt;Junit&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;에</description>
        
      
      
      
      <content:encoded><![CDATA[<h1 id="XV-JUnit-들여다보기"><a href="#XV-JUnit-들여다보기" class="headerlink" title="XV. JUnit 들여다보기"></a>XV. JUnit 들여다보기</h1><p>Junit</p><ul><li>에릭감마, 켄트백이 3시간만에 Junit 기초 구현</li></ul><h2 id="JUnit-프레임워크"><a href="#JUnit-프레임워크" class="headerlink" title="JUnit 프레임워크"></a>JUnit 프레임워크</h2><ul><li><p>책에 있는 ComparisonCompactor.java 따라해보기</p></li><li><p>ComparisonCompactor : <a href="https://github.com/junit-team/junit4/blob/main/src/main/java/junit/framework/ComparisonCompactor.java">https://github.com/junit-team/junit4/blob/main/src/main/java/junit/framework/ComparisonCompactor.java</a></p></li><li><p>ComparisonCompactorTest : <a href="https://github.com/junit-team/junit4/blob/main/src/test/java/junit/tests/framework/ComparisonCompactorTest.java">https://github.com/junit-team/junit4/blob/main/src/test/java/junit/tests/framework/ComparisonCompactorTest.java</a></p></li><li><p>직접해보기 : <a href="https://github.com/rkaehdaos/cleancode-ch15-Junit">https://github.com/rkaehdaos/cleancode-ch15-Junit</a></p></li></ul><figure class="highlight java"><figcaption><span>ComparisonCompactor.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> me.rkaehdaos;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> junit.framework.Assert;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ComparisonCompactor</span> {</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">ELLIPSIS</span> <span class="operator">=</span> <span class="string">"..."</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELTA_END</span> <span class="operator">=</span> <span class="string">"]"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DELTA_START</span> <span class="operator">=</span> <span class="string">"["</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> fContextLength;</span><br><span class="line">    <span class="keyword">private</span> String fExpected;</span><br><span class="line">    <span class="keyword">private</span> String fActual;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> fPrefix;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> fSuffix;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ComparisonCompactor</span><span class="params">(<span class="type">int</span> contextLength, String expected, String actual)</span> {</span><br><span class="line">        fContextLength = contextLength;</span><br><span class="line">        fExpected = expected;</span><br><span class="line">        fActual = actual;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings("deprecation")</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">compact</span><span class="params">(String message)</span> {</span><br><span class="line">        <span class="keyword">if</span> (fExpected == <span class="literal">null</span> || fActual == <span class="literal">null</span> || areStringsEqual()) {</span><br><span class="line">            <span class="keyword">return</span> Assert.format(message, fExpected, fActual);</span><br><span class="line">        }</span><br><span class="line"></span><br><span class="line">        findCommonPrefix();</span><br><span class="line">        findCommonSuffix();</span><br><span class="line">        <span class="type">String</span> <span class="variable">expected</span> <span class="operator">=</span> compactString(fExpected);</span><br><span class="line">        <span class="type">String</span> <span class="variable">actual</span> <span class="operator">=</span> compactString(fActual);</span><br><span class="line">        <span class="keyword">return</span> Assert.format(message, expected, actual);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">compactString</span><span class="params">(String source)</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> DELTA_START + source.substring(fPrefix, source.length() - fSuffix + <span class="number">1</span>) + DELTA_END;</span><br><span class="line">        <span class="keyword">if</span> (fPrefix &gt; <span class="number">0</span>) {</span><br><span class="line">            result = computeCommonPrefix() + result;</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">if</span> (fSuffix &gt; <span class="number">0</span>) {</span><br><span class="line">            result = result + computeCommonSuffix();</span><br><span class="line">        }</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">findCommonPrefix</span><span class="params">()</span> {</span><br><span class="line">        fPrefix = <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> Math.min(fExpected.length(), fActual.length());</span><br><span class="line">        <span class="keyword">for</span> (; fPrefix &lt; end; fPrefix++) {</span><br><span class="line">            <span class="keyword">if</span> (fExpected.charAt(fPrefix) != fActual.charAt(fPrefix)) {</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">findCommonSuffix</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">expectedSuffix</span> <span class="operator">=</span> fExpected.length() - <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">actualSuffix</span> <span class="operator">=</span> fActual.length() - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span> (; actualSuffix &gt;= fPrefix &amp;&amp; expectedSuffix &gt;= fPrefix; actualSuffix--, expectedSuffix--) {</span><br><span class="line">            <span class="keyword">if</span> (fExpected.charAt(expectedSuffix) != fActual.charAt(actualSuffix)) {</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">        fSuffix = fExpected.length() - expectedSuffix;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">computeCommonPrefix</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> (fPrefix &gt; fContextLength ? ELLIPSIS : <span class="string">""</span>) + fExpected.substring(Math.max(<span class="number">0</span>, fPrefix - fContextLength), fPrefix);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">computeCommonSuffix</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> Math.min(fExpected.length() - fSuffix + <span class="number">1</span> + fContextLength, fExpected.length());</span><br><span class="line">        <span class="keyword">return</span> fExpected.substring(fExpected.length() - fSuffix + <span class="number">1</span>, end) + (fExpected.length() - fSuffix + <span class="number">1</span> &lt; fExpected.length() - fContextLength ? ELLIPSIS : <span class="string">""</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">areStringsEqual</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> fExpected.equals(fActual);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>ComparisonCompactorTest.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> me.rkaehdaos;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> junit.framework.TestCase;</span><br><span class="line"></span><br><span class="line"><span class="comment">//import static org.junit.Assert.*;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ComparisonCompactorTest</span> <span class="keyword">extends</span> <span class="title class_">TestCase</span> {</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testMessage</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">0</span>, <span class="string">"b"</span>, <span class="string">"c"</span>).compact(<span class="string">"a"</span>);</span><br><span class="line">        <span class="comment">//인텔리제이는 Equals로 단순화 추천.. 일단 원래 코드 남김</span></span><br><span class="line">        <span class="comment">//        assertEquals("a expected:&lt;[b]&gt; but was:&lt;[c]&gt;", failure);</span></span><br><span class="line">        assertTrue(<span class="string">"a expected:&lt;[b]&gt; but was:&lt;[c]&gt;"</span>.equals(failure));</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testStartSame</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">1</span>, <span class="string">"ba"</span>, <span class="string">"bc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;b[a]&gt; but was:&lt;b[c]&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testEndSame</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">1</span>, <span class="string">"ab"</span>, <span class="string">"cb"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;[a]b&gt; but was:&lt;[c]b&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSame</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">1</span>, <span class="string">"ab"</span>, <span class="string">"ab"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;ab&gt; but was:&lt;ab&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testNoContextStartAndEndSame</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">0</span>, <span class="string">"abc"</span>, <span class="string">"adc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;...[b]...&gt; but was:&lt;...[d]...&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testStartAndEndContext</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">1</span>, <span class="string">"abc"</span>, <span class="string">"adc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;a[b]c&gt; but was:&lt;a[d]c&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testStartAndEndContextWithEllipses</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">1</span>, <span class="string">"abcde"</span>, <span class="string">"abfde"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;...b[c]d...&gt; but was:&lt;...b[f]d...&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorStartSameComplete</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">2</span>, <span class="string">"ab"</span>, <span class="string">"abc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;ab[]&gt; but was:&lt;ab[c]&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorEndSameComplete</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">0</span>, <span class="string">"bc"</span>, <span class="string">"abc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;[]...&gt; but was:&lt;[a]...&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorEndSameCompleteContext</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">2</span>, <span class="string">"bc"</span>, <span class="string">"abc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;[]bc&gt; but was:&lt;[a]bc&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorOverlappingMatches</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">0</span>, <span class="string">"abc"</span>, <span class="string">"abbc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;...[]...&gt; but was:&lt;...[b]...&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorOverlappingMatchesContext</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">2</span>, <span class="string">"abc"</span>, <span class="string">"abbc"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;ab[]c&gt; but was:&lt;ab[b]c&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorOverlappingMatches2</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">0</span>, <span class="string">"abcdde"</span>, <span class="string">"abcde"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;...[d]...&gt; but was:&lt;...[]...&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorOverlappingMatches2Context</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">2</span>, <span class="string">"abcdde"</span>, <span class="string">"abcde"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;...cd[d]e&gt; but was:&lt;...cd[]e&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorWithActualNull</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">0</span>, <span class="string">"a"</span>, <span class="literal">null</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;a&gt; but was:&lt;null&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorWithActualNullContext</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">2</span>, <span class="string">"a"</span>, <span class="literal">null</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;a&gt; but was:&lt;null&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorWithExpectedNull</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">0</span>, <span class="literal">null</span>, <span class="string">"a"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;null&gt; but was:&lt;a&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testComparisonErrorWithExpectedNullContext</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">2</span>, <span class="literal">null</span>, <span class="string">"a"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;null&gt; but was:&lt;a&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testBug609972</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">failure</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComparisonCompactor</span>(<span class="number">10</span>, <span class="string">"S&amp;P500"</span>, <span class="string">"0"</span>).compact(<span class="literal">null</span>);</span><br><span class="line">        assertEquals(<span class="string">"expected:&lt;[S&amp;P50]0&gt; but was:&lt;[]0&gt;"</span>, failure);</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="직접-테스트"><a href="#직접-테스트" class="headerlink" title="직접 테스트"></a>직접 테스트</h2><ul><li>code : <a href="https://github.com/rkaehdaos/cleancode-ch15-Junit">https://github.com/rkaehdaos/cleancode-ch15-Junit</a></li><li>project : <a href="https://github.com/rkaehdaos/cleancode-ch15-Junit/projects/1">https://github.com/rkaehdaos/cleancode-ch15-Junit/projects/1</a></li><li>들여다보기 부분 : <a href="https://github.com/rkaehdaos/cleancode-ch15-Junit/issues/1">https://github.com/rkaehdaos/cleancode-ch15-Junit/issues/1</a></li><li>고쳐보자 부분 : <a href="https://github.com/rkaehdaos/cleancode-ch15-Junit/issues/2">https://github.com/rkaehdaos/cleancode-ch15-Junit/issues/2</a></li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li>처음으로 본 제대로 된 코드</li><li>리팩터링 책의 예제보다 훨씬 깔끔해보임</li><li>테스트도 합리적..이고 양도 좀 많긴 함</li><li>이왕이면 위험 부분과 경계 영역 테스트가 좀 더 많아야 하지 않을까 하고 생각.</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch15/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch14/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch14/</guid>
      <pubDate>Wed, 09 Feb 2022 00:56:22 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;XIV-점진적-개선-SUCCESSIVE-REFINEMENT&quot;&gt;&lt;a href=&quot;#XIV-점진적-개선-SUCCESSIVE-REFIN</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="XIV-점진적-개선-SUCCESSIVE-REFINEMENT"><a href="#XIV-점진적-개선-SUCCESSIVE-REFINEMENT" class="headerlink" title="XIV. 점진적 개선(SUCCESSIVE REFINEMENT)"></a>XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</h1><blockquote><p>SUCCESSIVE REFINEMENT : 연속적인 정제</p></blockquote><ul><li>해당 챕터는 점진적인 개선을 보여주는 사례 연구</li><li>우선, 출발은 좋았으나 확장성이 부족했던 모듈을 소개</li><li>그런 다음, 모듈을 개선하고 정리하는 단계를 살펴본다</li></ul><p> 예시</p><ul><li>프로그램을 짜다 보면 종종 명령행 인수의 구문을 분석할 필요가 생긴다</li><li>편리한 유틸리티가 없다면 main 함수로 넘어오는 문자열 배열을 직접 분석하게 된다</li><li>내 사정에 딱 맞는 유틸리티가 없다면? 물론 직접 짜겠다고 결심한다. 새로 짠 유틸리티를 Args라 부르겠다</li></ul><figure class="highlight java"><figcaption><span>간단한 Args 사용법</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">  <span class="keyword">try</span> {</span><br><span class="line">    <span class="type">Args</span> <span class="variable">arg</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Args</span>(<span class="string">"l,p#,d*"</span>, args);</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">logging</span> <span class="operator">=</span> arg.getBoolean(<span class="string">'l'</span>);</span><br><span class="line">    <span class="type">int</span> <span class="variable">port</span> <span class="operator">=</span> arg.getInt(<span class="string">'p'</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">directory</span> <span class="operator">=</span> arg.getString(<span class="string">'d'</span>);</span><br><span class="line">    executeApplication(logging, port, directory);</span><br><span class="line">  } <span class="keyword">catch</span> (ArgsException e) {</span><br><span class="line">    System.out.print(<span class="string">"Argument error: %s\n"</span>, e.errorMessage());</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>설명</p><ul><li>명령어 인수 구문 분석기</li><li>Args는 사용법이 간단하다</li><li>Args 생성자에 (입력 파라미터로 들어온) 인수 문자열과 형식 문자열을 넘겨 Args 인스턴스를 생성<ul><li>첫 파라미터 : 형식 또는 스키마를 지정, 예시에서는 부울인수, 정수 인수, 문자열인수</li><li>두번째 파라미터 : main으로 넘어온 명령행 인수배열 자체</li></ul></li><li>명령행 인수 구문 분석 시도<ul><li>성공? → 정상적으로 인스턴스 생성 → 그 후 Args 인스턴스에다 인수 값을 질의(getter메서드)</li><li>실패? → ArgsException 발생 → 형식 문자열이나 명령행 인수 자체에 문제</li></ul></li></ul><h2 id="Args-구현"><a href="#Args-구현" class="headerlink" title="Args 구현"></a>Args 구현</h2><figure class="highlight java"><figcaption><span>14-2 Args.java 대체로 깔끔한 구조에 잘짜인</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.objectmentor.utilities.args;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="keyword">static</span> com.objectmentor.utilities.args.ArgsException.ErrorCode.*; </span><br><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Args</span> {</span><br><span class="line">  <span class="keyword">private</span> Map&lt;Character, ArgumentMarshaler&gt; marshalers;</span><br><span class="line">  <span class="keyword">private</span> Set&lt;Character&gt; argsFound;</span><br><span class="line">  <span class="keyword">private</span> ListIterator&lt;String&gt; currentArgument;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">Args</span><span class="params">(String schema, String[] args)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    marshalers = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;Character, ArgumentMarshaler&gt;(); </span><br><span class="line">    argsFound = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;Character&gt;();</span><br><span class="line">    </span><br><span class="line">    parseSchema(schema);</span><br><span class="line">    parseArgumentStrings(Arrays.asList(args)); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseSchema</span><span class="params">(String schema)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">for</span> (String element : schema.split(<span class="string">","</span>))</span><br><span class="line">      <span class="keyword">if</span> (element.length() &gt; <span class="number">0</span>) </span><br><span class="line">        parseSchemaElement(element.trim());</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseSchemaElement</span><span class="params">(String element)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="type">char</span> <span class="variable">elementId</span> <span class="operator">=</span> element.charAt(<span class="number">0</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">elementTail</span> <span class="operator">=</span> element.substring(<span class="number">1</span>);</span><br><span class="line">    validateSchemaElementId(elementId);</span><br><span class="line">    <span class="keyword">if</span> (elementTail.length() == <span class="number">0</span>)</span><br><span class="line">      marshalers.put(elementId, <span class="keyword">new</span> <span class="title class_">BooleanArgumentMarshaler</span>());</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (elementTail.equals(<span class="string">"*"</span>)) </span><br><span class="line">      marshalers.put(elementId, <span class="keyword">new</span> <span class="title class_">StringArgumentMarshaler</span>());</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (elementTail.equals(<span class="string">"#"</span>))</span><br><span class="line">      marshalers.put(elementId, <span class="keyword">new</span> <span class="title class_">IntegerArgumentMarshaler</span>());</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (elementTail.equals(<span class="string">"##"</span>)) </span><br><span class="line">      marshalers.put(elementId, <span class="keyword">new</span> <span class="title class_">DoubleArgumentMarshaler</span>());</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (elementTail.equals(<span class="string">"[*]"</span>))</span><br><span class="line">      marshalers.put(elementId, <span class="keyword">new</span> <span class="title class_">StringArrayArgumentMarshaler</span>());</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>(INVALID_ARGUMENT_FORMAT, elementId, elementTail);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">validateSchemaElementId</span><span class="params">(<span class="type">char</span> elementId)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">if</span> (!Character.isLetter(elementId))</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>(INVALID_ARGUMENT_NAME, elementId, <span class="literal">null</span>); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseArgumentStrings</span><span class="params">(List&lt;String&gt; argsList)</span> <span class="keyword">throws</span> ArgsException {</span><br><span class="line">    <span class="keyword">for</span> (currentArgument = argsList.listIterator(); currentArgument.hasNext();) {</span><br><span class="line">      <span class="type">String</span> <span class="variable">argString</span> <span class="operator">=</span> currentArgument.next(); </span><br><span class="line">      <span class="keyword">if</span> (argString.startsWith(<span class="string">"-"</span>)) {</span><br><span class="line">        parseArgumentCharacters(argString.substring(<span class="number">1</span>)); </span><br><span class="line">      } <span class="keyword">else</span> {</span><br><span class="line">        currentArgument.previous();</span><br><span class="line">        <span class="keyword">break</span>; </span><br><span class="line">      }</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseArgumentCharacters</span><span class="params">(String argChars)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; argChars.length(); i++)</span><br><span class="line">      parseArgumentCharacter(argChars.charAt(i)); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseArgumentCharacter</span><span class="params">(<span class="type">char</span> argChar)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="type">ArgumentMarshaler</span> <span class="variable">m</span> <span class="operator">=</span> marshalers.get(argChar);</span><br><span class="line">    <span class="keyword">if</span> (m == <span class="literal">null</span>) {</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>(UNEXPECTED_ARGUMENT, argChar, <span class="literal">null</span>); </span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">      argsFound.add(argChar); </span><br><span class="line">      <span class="keyword">try</span> {</span><br><span class="line">        m.set(currentArgument); </span><br><span class="line">      } <span class="keyword">catch</span> (ArgsException e) {</span><br><span class="line">        e.setErrorArgumentId(argChar);</span><br><span class="line">        <span class="keyword">throw</span> e; </span><br><span class="line">      }</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">has</span><span class="params">(<span class="type">char</span> arg)</span> { </span><br><span class="line">    <span class="keyword">return</span> argsFound.contains(arg);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">nextArgument</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">return</span> currentArgument.nextIndex();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">getBoolean</span><span class="params">(<span class="type">char</span> arg)</span> {</span><br><span class="line">    <span class="keyword">return</span> BooleanArgumentMarshaler.getValue(marshalers.get(arg));</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getString</span><span class="params">(<span class="type">char</span> arg)</span> {</span><br><span class="line">    <span class="keyword">return</span> StringArgumentMarshaler.getValue(marshalers.get(arg));</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getInt</span><span class="params">(<span class="type">char</span> arg)</span> {</span><br><span class="line">    <span class="keyword">return</span> IntegerArgumentMarshaler.getValue(marshalers.get(arg));</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">getDouble</span><span class="params">(<span class="type">char</span> arg)</span> {</span><br><span class="line">    <span class="keyword">return</span> DoubleArgumentMarshaler.getValue(marshalers.get(arg));</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String[] getStringArray(<span class="type">char</span> arg) {</span><br><span class="line">    <span class="keyword">return</span> StringArrayArgumentMarshaler.getValue(marshalers.get(arg));</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>특징 : 코드가 아래로 읽힌다</p><ul><li>잘 읽었으면 <code>ArgumentMarshaler</code> 인터페이스와 파생 클래스의 기능을 이해했을 것</li></ul><figure class="highlight java"><figcaption><span>14-3 ArgumentMarshaler.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ArgumentMarshaler</span> {</span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(Iterator&lt;String&gt; currentArgument)</span> <span class="keyword">throws</span> ArgsException;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>14-4 BooleanArgumentMarshaler.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BooleanArgumentMarshaler</span> <span class="keyword">implements</span> <span class="title class_">ArgumentMarshaler</span> { </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">booleanValue</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(Iterator&lt;String&gt; currentArgument)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    booleanValue = <span class="literal">true</span>;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">getValue</span><span class="params">(ArgumentMarshaler am)</span> {</span><br><span class="line">    <span class="keyword">if</span> (am != <span class="literal">null</span> &amp;&amp; am <span class="keyword">instanceof</span> BooleanArgumentMarshaler)</span><br><span class="line">      <span class="keyword">return</span> ((BooleanArgumentMarshaler) am).booleanValue; </span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>StringArgumentMarshaler.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="keyword">static</span> com.objectmentor.utilities.args.ArgsException.ErrorCode.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StringArgumentMarshaler</span> <span class="keyword">implements</span> <span class="title class_">ArgumentMarshaler</span> { </span><br><span class="line">  <span class="keyword">private</span> <span class="type">String</span> <span class="variable">stringValue</span> <span class="operator">=</span> <span class="string">""</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(Iterator&lt;String&gt; currentArgument)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      stringValue = currentArgument.next(); </span><br><span class="line">    } <span class="keyword">catch</span> (NoSuchElementException e) {</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>(MISSING_STRING); </span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">getValue</span><span class="params">(ArgumentMarshaler am)</span> {</span><br><span class="line">    <span class="keyword">if</span> (am != <span class="literal">null</span> &amp;&amp; am <span class="keyword">instanceof</span> StringArgumentMarshaler)</span><br><span class="line">      <span class="keyword">return</span> ((StringArgumentMarshaler) am).stringValue; </span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">return</span> <span class="string">""</span>; </span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>IntegerArgumentMarshaler.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="keyword">static</span> com.objectmentor.utilities.args.ArgsException.ErrorCode.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IntegerArgumentMarshaler</span> <span class="keyword">implements</span> <span class="title class_">ArgumentMarshaler</span> { </span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> <span class="variable">intValue</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">set</span><span class="params">(Iterator&lt;String&gt; currentArgument)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="type">String</span> <span class="variable">parameter</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      parameter = currentArgument.next();</span><br><span class="line">      intValue = Integer.parseInt(parameter);</span><br><span class="line">    } <span class="keyword">catch</span> (NoSuchElementException e) {</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>(MISSING_INTEGER);</span><br><span class="line">    } <span class="keyword">catch</span> (NumberFormatException e) {</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>(INVALID_INTEGER, parameter); </span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">getValue</span><span class="params">(ArgumentMarshaler am)</span> {</span><br><span class="line">    <span class="keyword">if</span> (am != <span class="literal">null</span> &amp;&amp; am <span class="keyword">instanceof</span> IntegerArgumentMarshaler)</span><br><span class="line">      <span class="keyword">return</span> ((IntegerArgumentMarshaler) am).intValue; </span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>; </span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li><code>DoubleArgumentMarshaler</code>, <code>StringArrayArgumentMarshaler</code>는 생략, 연습문제</li></ul><figure class="highlight java"><figcaption><span>ArgsException.java 오류 코드 상수 정의</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="keyword">static</span> com.objectmentor.utilities.args.ArgsException.ErrorCode.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArgsException</span> <span class="keyword">extends</span> <span class="title class_">Exception</span> { </span><br><span class="line">  <span class="keyword">private</span> <span class="type">char</span> <span class="variable">errorArgumentId</span> <span class="operator">=</span> <span class="string">'\0'</span>; </span><br><span class="line">  <span class="keyword">private</span> <span class="type">String</span> <span class="variable">errorParameter</span> <span class="operator">=</span> <span class="literal">null</span>; </span><br><span class="line">  <span class="keyword">private</span> <span class="type">ErrorCode</span> <span class="variable">errorCode</span> <span class="operator">=</span> OK;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">ArgsException</span><span class="params">()</span> {}</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">ArgsException</span><span class="params">(String message)</span> {<span class="built_in">super</span>(message);}</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">ArgsException</span><span class="params">(ErrorCode errorCode)</span> { </span><br><span class="line">    <span class="built_in">this</span>.errorCode = errorCode;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">ArgsException</span><span class="params">(ErrorCode errorCode, String errorParameter)</span> { </span><br><span class="line">    <span class="built_in">this</span>.errorCode = errorCode;</span><br><span class="line">    <span class="built_in">this</span>.errorParameter = errorParameter;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">ArgsException</span><span class="params">(ErrorCode errorCode, <span class="type">char</span> errorArgumentId, String errorParameter)</span> {</span><br><span class="line">    <span class="built_in">this</span>.errorCode = errorCode; </span><br><span class="line">    <span class="built_in">this</span>.errorParameter = errorParameter; </span><br><span class="line">    <span class="built_in">this</span>.errorArgumentId = errorArgumentId;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">char</span> <span class="title function_">getErrorArgumentId</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> errorArgumentId;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setErrorArgumentId</span><span class="params">(<span class="type">char</span> errorArgumentId)</span> { </span><br><span class="line">    <span class="built_in">this</span>.errorArgumentId = errorArgumentId;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getErrorParameter</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> errorParameter;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setErrorParameter</span><span class="params">(String errorParameter)</span> { </span><br><span class="line">    <span class="built_in">this</span>.errorParameter = errorParameter;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> ErrorCode <span class="title function_">getErrorCode</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> errorCode;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setErrorCode</span><span class="params">(ErrorCode errorCode)</span> { </span><br><span class="line">    <span class="built_in">this</span>.errorCode = errorCode;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">errorMessage</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">switch</span> (errorCode) {</span><br><span class="line">      <span class="keyword">case</span> OK:</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"TILT: Should not get here."</span>;</span><br><span class="line">      <span class="keyword">case</span> UNEXPECTED_ARGUMENT:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Argument -%c unexpected."</span>, errorArgumentId);</span><br><span class="line">      <span class="keyword">case</span> MISSING_STRING:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Could not find string parameter for -%c."</span>, errorArgumentId);</span><br><span class="line">      <span class="keyword">case</span> INVALID_INTEGER:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Argument -%c expects an integer but was '%s'."</span>, errorArgumentId, errorParameter);</span><br><span class="line">      <span class="keyword">case</span> MISSING_INTEGER:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Could not find integer parameter for -%c."</span>, errorArgumentId);</span><br><span class="line">      <span class="keyword">case</span> INVALID_DOUBLE:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Argument -%c expects a double but was '%s'."</span>, errorArgumentId, errorParameter);</span><br><span class="line">      <span class="keyword">case</span> MISSING_DOUBLE:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Could not find double parameter for -%c."</span>, errorArgumentId); </span><br><span class="line">      <span class="keyword">case</span> INVALID_ARGUMENT_NAME:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"'%c' is not a valid argument name."</span>, errorArgumentId);</span><br><span class="line">      <span class="keyword">case</span> INVALID_ARGUMENT_FORMAT:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"'%s' is not a valid argument format."</span>, errorParameter);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="string">""</span>; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">ErrorCode</span> {</span><br><span class="line">    OK, INVALID_ARGUMENT_FORMAT, UNEXPECTED_ARGUMENT, INVALID_ARGUMENT_NAME, </span><br><span class="line">    MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, MISSING_DOUBLE, INVALID_DOUBLE</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>단순 개념 구현에 너무 많은 코드</p><ul><li>장환한 언어 자바 → 정적 타입 언어 → 타입 시스템 만족을 위해 많은 단어 필요</li><li>네이밍, 함수 크기, 형식에 주목 → 전반적으로 깔끔하고 잘짜인 프로그램</li></ul><p>예) 날짜 인수, 복소수 인수 등 새로운 인스타입 추가</p><ul><li>ArgumentMarshaler에서 새 클래스를 파생해 getXXX 함수를 추가</li><li>parseSchemaElement 함수에 새 case 문만 추가하면 끝</li><li>필요시 ArgsException.ErrorCode를 만들고 새 오류 메시지를 추가</li></ul><h3 id="어떻게-짰느냐고"><a href="#어떻게-짰느냐고" class="headerlink" title="어떻게 짰느냐고?"></a>어떻게 짰느냐고?</h3><p>한번에 뚝딱 나온 것이 아님</p><ul><li>수십 년의 교훈 : 프로그래밍은 과학보다 공예(craft)에 가깝다</li><li><strong>깨끗한 코드를 짜려면 먼저 지저분한 코드를 짠 뒤에 정리해야 한다는 의미</strong></li><li>작문도 1차 초안, 2차 초안 최종안을 만든다</li><li>대부분 신참은 대다수의 초딩처럼 이 충고를 따르지않음<ul><li>무조건 돌아가는 프로그램 목표 → 일단 ‘돌아가면’ 다음 업무 → ‘돌아가는’ 프로그램은 상태가 어떻든 LET IT BE → 자살 행위</li></ul></li></ul><h2 id="Args-1차-초안"><a href="#Args-1차-초안" class="headerlink" title="Args: 1차 초안"></a>Args: 1차 초안</h2><figure class="highlight java"><figcaption><span>Args.java 1차 초안</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.text.ParseException; </span><br><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Args</span> {</span><br><span class="line">  <span class="keyword">private</span> String schema;</span><br><span class="line">  <span class="keyword">private</span> String[] args;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">valid</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">  <span class="keyword">private</span> Set&lt;Character&gt; unexpectedArguments = <span class="keyword">new</span> <span class="title class_">TreeSet</span>&lt;Character&gt;(); </span><br><span class="line">  <span class="keyword">private</span> Map&lt;Character, Boolean&gt; booleanArgs = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;Character, Boolean&gt;();</span><br><span class="line">  <span class="keyword">private</span> Map&lt;Character, String&gt; stringArgs = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;Character, String&gt;(); </span><br><span class="line">  <span class="keyword">private</span> Map&lt;Character, Integer&gt; intArgs = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;Character, Integer&gt;(); </span><br><span class="line">  <span class="keyword">private</span> Set&lt;Character&gt; argsFound = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;Character&gt;();</span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> currentArgument;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">char</span> <span class="variable">errorArgumentId</span> <span class="operator">=</span> <span class="string">'\0'</span>;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">String</span> <span class="variable">errorParameter</span> <span class="operator">=</span> <span class="string">"TILT"</span>;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">ErrorCode</span> <span class="variable">errorCode</span> <span class="operator">=</span> ErrorCode.OK;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">enum</span> <span class="title class_">ErrorCode</span> {</span><br><span class="line">    OK, MISSING_STRING, MISSING_INTEGER, INVALID_INTEGER, UNEXPECTED_ARGUMENT}</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">Args</span><span class="params">(String schema, String[] args)</span> <span class="keyword">throws</span> ParseException { </span><br><span class="line">    <span class="built_in">this</span>.schema = schema;</span><br><span class="line">    <span class="built_in">this</span>.args = args;</span><br><span class="line">    valid = parse();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">parse</span><span class="params">()</span> <span class="keyword">throws</span> ParseException { </span><br><span class="line">    <span class="keyword">if</span> (schema.length() == <span class="number">0</span> &amp;&amp; args.length == <span class="number">0</span>)</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">    parseSchema(); </span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      parseArguments();</span><br><span class="line">    } <span class="keyword">catch</span> (ArgsException e) {</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> valid;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">parseSchema</span><span class="params">()</span> <span class="keyword">throws</span> ParseException { </span><br><span class="line">    <span class="keyword">for</span> (String element : schema.split(<span class="string">","</span>)) {</span><br><span class="line">      <span class="keyword">if</span> (element.length() &gt; <span class="number">0</span>) {</span><br><span class="line">        <span class="type">String</span> <span class="variable">trimmedElement</span> <span class="operator">=</span> element.trim(); </span><br><span class="line">        parseSchemaElement(trimmedElement);</span><br><span class="line">      } </span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseSchemaElement</span><span class="params">(String element)</span> <span class="keyword">throws</span> ParseException { </span><br><span class="line">    <span class="type">char</span> <span class="variable">elementId</span> <span class="operator">=</span> element.charAt(<span class="number">0</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">elementTail</span> <span class="operator">=</span> element.substring(<span class="number">1</span>); </span><br><span class="line">    validateSchemaElementId(elementId);</span><br><span class="line">    <span class="keyword">if</span> (isBooleanSchemaElement(elementTail)) </span><br><span class="line">      parseBooleanSchemaElement(elementId);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (isStringSchemaElement(elementTail)) </span><br><span class="line">      parseStringSchemaElement(elementId);</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (isIntegerSchemaElement(elementTail)) </span><br><span class="line">      parseIntegerSchemaElement(elementId);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ParseException</span>(String.format(<span class="string">"Argument: %c has invalid format: %s."</span>, </span><br><span class="line">        elementId, elementTail), <span class="number">0</span>);</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">validateSchemaElementId</span><span class="params">(<span class="type">char</span> elementId)</span> <span class="keyword">throws</span> ParseException { </span><br><span class="line">    <span class="keyword">if</span> (!Character.isLetter(elementId)) {</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ParseException</span>(<span class="string">"Bad character:"</span> + elementId + <span class="string">"in Args format: "</span> + schema, <span class="number">0</span>);</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseBooleanSchemaElement</span><span class="params">(<span class="type">char</span> elementId)</span> { </span><br><span class="line">    booleanArgs.put(elementId, <span class="literal">false</span>);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseIntegerSchemaElement</span><span class="params">(<span class="type">char</span> elementId)</span> { </span><br><span class="line">    intArgs.put(elementId, <span class="number">0</span>);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseStringSchemaElement</span><span class="params">(<span class="type">char</span> elementId)</span> { </span><br><span class="line">    stringArgs.put(elementId, <span class="string">""</span>);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isStringSchemaElement</span><span class="params">(String elementTail)</span> { </span><br><span class="line">    <span class="keyword">return</span> elementTail.equals(<span class="string">"*"</span>);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isBooleanSchemaElement</span><span class="params">(String elementTail)</span> { </span><br><span class="line">    <span class="keyword">return</span> elementTail.length() == <span class="number">0</span>;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isIntegerSchemaElement</span><span class="params">(String elementTail)</span> { </span><br><span class="line">    <span class="keyword">return</span> elementTail.equals(<span class="string">"#"</span>);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">parseArguments</span><span class="params">()</span> <span class="keyword">throws</span> ArgsException {</span><br><span class="line">    <span class="keyword">for</span> (currentArgument = <span class="number">0</span>; currentArgument &lt; args.length; currentArgument++) {</span><br><span class="line">      <span class="type">String</span> <span class="variable">arg</span> <span class="operator">=</span> args[currentArgument];</span><br><span class="line">      parseArgument(arg); </span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseArgument</span><span class="params">(String arg)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">if</span> (arg.startsWith(<span class="string">"-"</span>))</span><br><span class="line">      parseElements(arg); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseElements</span><span class="params">(String arg)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; arg.length(); i++)</span><br><span class="line">      parseElement(arg.charAt(i)); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">parseElement</span><span class="params">(<span class="type">char</span> argChar)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">if</span> (setArgument(argChar))</span><br><span class="line">      argsFound.add(argChar); </span><br><span class="line">    <span class="keyword">else</span> </span><br><span class="line">      unexpectedArguments.add(argChar); </span><br><span class="line">      errorCode = ErrorCode.UNEXPECTED_ARGUMENT; </span><br><span class="line">      valid = <span class="literal">false</span>;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">setArgument</span><span class="params">(<span class="type">char</span> argChar)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    <span class="keyword">if</span> (isBooleanArg(argChar))</span><br><span class="line">      setBooleanArg(argChar, <span class="literal">true</span>); </span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (isStringArg(argChar))</span><br><span class="line">      setStringArg(argChar); </span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (isIntArg(argChar))</span><br><span class="line">      setIntArg(argChar); </span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isIntArg</span><span class="params">(<span class="type">char</span> argChar)</span> {</span><br><span class="line">    <span class="keyword">return</span> intArgs.containsKey(argChar);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setIntArg</span><span class="params">(<span class="type">char</span> argChar)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    currentArgument++;</span><br><span class="line">    <span class="type">String</span> <span class="variable">parameter</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      parameter = args[currentArgument];</span><br><span class="line">      intArgs.put(argChar, <span class="keyword">new</span> <span class="title class_">Integer</span>(parameter)); </span><br><span class="line">    } <span class="keyword">catch</span> (ArrayIndexOutOfBoundsException e) {</span><br><span class="line">      valid = <span class="literal">false</span>;</span><br><span class="line">      errorArgumentId = argChar;</span><br><span class="line">      errorCode = ErrorCode.MISSING_INTEGER;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>();</span><br><span class="line">    } <span class="keyword">catch</span> (NumberFormatException e) {</span><br><span class="line">      valid = <span class="literal">false</span>;</span><br><span class="line">      errorArgumentId = argChar; </span><br><span class="line">      errorParameter = parameter;</span><br><span class="line">      errorCode = ErrorCode.INVALID_INTEGER; </span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>();</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setStringArg</span><span class="params">(<span class="type">char</span> argChar)</span> <span class="keyword">throws</span> ArgsException { </span><br><span class="line">    currentArgument++;</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      stringArgs.put(argChar, args[currentArgument]); </span><br><span class="line">    } <span class="keyword">catch</span> (ArrayIndexOutOfBoundsException e) {</span><br><span class="line">      valid = <span class="literal">false</span>;</span><br><span class="line">      errorArgumentId = argChar;</span><br><span class="line">      errorCode = ErrorCode.MISSING_STRING; </span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArgsException</span>();</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isStringArg</span><span class="params">(<span class="type">char</span> argChar)</span> { </span><br><span class="line">    <span class="keyword">return</span> stringArgs.containsKey(argChar);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setBooleanArg</span><span class="params">(<span class="type">char</span> argChar, <span class="type">boolean</span> value)</span> { </span><br><span class="line">    booleanArgs.put(argChar, value);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isBooleanArg</span><span class="params">(<span class="type">char</span> argChar)</span> { </span><br><span class="line">    <span class="keyword">return</span> booleanArgs.containsKey(argChar);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">cardinality</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> argsFound.size();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">usage</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">if</span> (schema.length() &gt; <span class="number">0</span>)</span><br><span class="line">      <span class="keyword">return</span> <span class="string">"-["</span> + schema + <span class="string">"]"</span>; </span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">return</span> <span class="string">""</span>; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">errorMessage</span><span class="params">()</span> <span class="keyword">throws</span> Exception { </span><br><span class="line">    <span class="keyword">switch</span> (errorCode) {</span><br><span class="line">      <span class="keyword">case</span> OK:</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">"TILT: Should not get here."</span>);</span><br><span class="line">      <span class="keyword">case</span> UNEXPECTED_ARGUMENT:</span><br><span class="line">        <span class="keyword">return</span> unexpectedArgumentMessage();</span><br><span class="line">      <span class="keyword">case</span> MISSING_STRING:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Could not find string parameter for -%c."</span>, errorArgumentId);</span><br><span class="line">      <span class="keyword">case</span> INVALID_INTEGER:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Argument -%c expects an integer but was '%s'."</span>, errorArgumentId, errorParameter);</span><br><span class="line">      <span class="keyword">case</span> MISSING_INTEGER:</span><br><span class="line">        <span class="keyword">return</span> String.format(<span class="string">"Could not find integer parameter for -%c."</span>, errorArgumentId);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="string">""</span>; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">unexpectedArgumentMessage</span><span class="params">()</span> {</span><br><span class="line">    <span class="type">StringBuffer</span> <span class="variable">message</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuffer</span>(<span class="string">"Argument(s) -"</span>); </span><br><span class="line">    <span class="keyword">for</span> (<span class="type">char</span> c : unexpectedArguments) {</span><br><span class="line">      message.append(c); </span><br><span class="line">    }</span><br><span class="line">    message.append(<span class="string">" unexpected."</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> message.toString(); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">falseIfNull</span><span class="params">(Boolean b)</span> { </span><br><span class="line">    <span class="keyword">return</span> b != <span class="literal">null</span> &amp;&amp; b;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">zeroIfNull</span><span class="params">(Integer i)</span> { </span><br><span class="line">    <span class="keyword">return</span> i == <span class="literal">null</span> ? <span class="number">0</span> : i;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">blankIfNull</span><span class="params">(String s)</span> { </span><br><span class="line">    <span class="keyword">return</span> s == <span class="literal">null</span> ? <span class="string">""</span> : s;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getString</span><span class="params">(<span class="type">char</span> arg)</span> { </span><br><span class="line">    <span class="keyword">return</span> blankIfNull(stringArgs.get(arg));</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getInt</span><span class="params">(<span class="type">char</span> arg)</span> {</span><br><span class="line">    <span class="keyword">return</span> zeroIfNull(intArgs.get(arg));</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">getBoolean</span><span class="params">(<span class="type">char</span> arg)</span> { </span><br><span class="line">    <span class="keyword">return</span> falseIfNull(booleanArgs.get(arg));</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">has</span><span class="params">(<span class="type">char</span> arg)</span> { </span><br><span class="line">    <span class="keyword">return</span> argsFound.contains(arg);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isValid</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> valid;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">class</span> <span class="title class_">ArgsException</span> <span class="keyword">extends</span> <span class="title class_">Exception</span> {</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>사실상 초안도 아닌 미완성</p><ul><li>함수 이름, 변수이름, 나름의 구조 → 나름의 노력의 증거</li><li>하지만 결과는 엉망<blockquote><p>책에서는 초안 이전의 그나마 나았던 코드에서 엉망이 되어가는 부분을 보여주고 있음</p></blockquote></li></ul><h3 id="그래서-멈췄다"><a href="#그래서-멈췄다" class="headerlink" title="그래서 멈췄다"></a>그래서 멈췄다</h3><p>완성을 멈추다</p><ul><li>추가할 인수 유형이 적어도 2개 → 더 나쁜 코드가 되는 자명한 사실</li><li>코드 리팩터링의 적기를 지금으로 판단</li></ul><h3 id="전체-리팩터링-진행"><a href="#전체-리팩터링-진행" class="headerlink" title="전체 리팩터링 진행"></a>전체 리팩터링 진행</h3><ul><li>github : <a href="https://github.com/rkaehdaos/cleancode-learning-successive-refinement">https://github.com/rkaehdaos/cleancode-learning-successive-refinement</a></li><li>project : <a href="https://github.com/rkaehdaos/cleancode-learning-successive-refinement/projects/1">https://github.com/rkaehdaos/cleancode-learning-successive-refinement/projects/1</a></li></ul><h3 id=""><a href="#" class="headerlink" title=""></a></h3><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li>나쁜 일정은 다시 짜면 된다. 나쁜 요구사항은 다시 정의하면 된다. 나쁜 팀 역학은 복구하면 된다.</li><li>하지만 나쁜 코드는 썩어 문드러진다.  점점 무게가 늘어나 팀의 발목을 잡는다</li><li>서두르다 영원히 자신의 운명을 지배할 악성 코드라는 굴레를 짊어진다</li><li>그저 돌아가는 코드만으로는 부족하다. 돌아가는 코드가 심하게 망가지는 사례는 흔하다. 단순히 돌아가는 코드에 만족하는 프로그래머는 전문가 정신이 부족하다.</li><li>나쁜 코드보다 더 오랫동안 더 심각하게 개발 프로젝트에 악영향을 미치는 요인도 없다</li><li>물론 나쁜 코드도 깨끗한 코드로 개선할 수 있다. 하지만 비용이 엄청나게 많이 든다<ul><li>코드가 썩어가며 모듈은 서로서로 얽히고설켜 뒤엉키고 숨겨진 의존성이 수도 없이 생긴다</li><li>오래된 의존성을 찾아내 깨려면 상당한 시간과 인내심이 필요하다</li></ul></li><li>반면 처음부터 코드를 깨끗하게 유지하기란 상대적으로 쉽다<ul><li>아침에 엉망으로 만든 코드를 오후에 정리하기는 어렵지 않다</li><li>더욱이 5분 전에 엉망으로 만든 코드는 지금 당장 정리하기 아주 쉽다</li></ul></li><li>그러므로 코드는 언제나 최대한 깔끔하고 단순하게 정리하자. 절대로 썩어가게 방치하면 안 된다.</li></ul><blockquote><ul><li>진짜 테스트 코드는 여기 있었네;; 반성</li></ul></blockquote><blockquote><ul><li>프로그램을 망치는 가장 좋은 방법 중 하나 ? 개선이라는 이름 아래 구조를 크게 뒤집는 행위</li></ul></blockquote><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch14/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] XIII. 동시성</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch13/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch13/</guid>
      <pubDate>Wed, 09 Feb 2022 00:56:00 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;XIII-동시성-Concurrency&quot;&gt;&lt;a href=&quot;#XIII-동시성-Concurrency&quot; class=&quot;headerlink</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="XIII-동시성-Concurrency"><a href="#XIII-동시성-Concurrency" class="headerlink" title="XIII. 동시성(Concurrency)"></a>XIII. 동시성(Concurrency)</h1><blockquote><p>“Objects are abstractions of processing. Threads are abstractions of schedule.”<br>객체는 처리의 추상화. 스레드는 일정의 추상화.</p><footer><strong>James O. Coplien</strong><cite>밥아저씨 지인</cite></footer></blockquote><p>동시성과 클린 코드의 양립은 아주 어렵다</p><ul><li>단일 스레드에서 동작하는 코드는 작성하기 쉽다</li><li>잘 동작하는 “것 처럼” 보이는 멀티 스레드 코드를 작성하기도 쉽다</li><li>부하를 받기 전까지 멀쩡하게 돌아간다</li></ul><h2 id="동시성-Concurrency-이-필요한-이유"><a href="#동시성-Concurrency-이-필요한-이유" class="headerlink" title="동시성(Concurrency)이 필요한 이유?"></a>동시성(Concurrency)이 필요한 이유?</h2><h4 id="동시성-x3D-what과-when을-분리하는-전략"><a href="#동시성-x3D-what과-when을-분리하는-전략" class="headerlink" title="동시성 = what과 when을 분리하는 전략"></a>동시성 = what과 when을 분리하는 전략</h4><ul><li>동시성은 coupling을 없애는 전략 : what과 when</li><li>스레드가 하나인 프로그램 : what과 when이 강하게 결합되어 있고 밀접<ul><li>단일 스레드 디버깅: breakpoint 정한 후 어느 정지점에 걸렸는가 시스템 파악</li></ul></li></ul><h4 id="what-when의-분리-→-어플리케이션의-구조와-효율의-극적-개선"><a href="#what-when의-분리-→-어플리케이션의-구조와-효율의-극적-개선" class="headerlink" title="what when의 분리 → 어플리케이션의 구조와 효율의 극적 개선"></a>what when의 분리 → 어플리케이션의 구조와 효율의 극적 개선</h4><ul><li>구조관점 : 거대한 loop가 아닌 작은 여러개의 협력 프로그램으로 보임 → 시스템 이해가 쉽고 문제 분리도 쉬움<ul><li>예시 ) 서블릿<ul><li>서블릿은 서블릿 컨테이너 안에서 돌아감 → 컨테이너가 동시성을 ‘부분적으로’ 관리</li><li>웹 요청시 마다 웹 서버는 비동기식으로 서블릿 실행</li><li>(이론적으로) 서블릿 개발자는 들어오는 모든 웹 요청 관리가 필요없음</li><li>(원칙적으로) 각 서블릿 스레드는 다른 서블릿 스레드와 무관하게 자신만의 세상에서 돌아간다</li></ul></li></ul></li></ul><p>동시성이 이렇게 간단하면 이번 챕터가 필요 없었을 것</p><ul><li>서블릿이 제공하는 의존성 해소는 완벽하지 않음</li><li>웹 컨테이너의 deCoupling → 완벽과 거리가 아주 멀다</li><li>서블릿 프로그래머는 동시성을 정확히 구현하도록 각별한 주의와 노력을 기울여야 한다.</li><li>그럼에도 서블릿 모델이 제공하는 구조적 이점은 아주 크다.</li></ul><p>구조적 개선만을 위해 동시성을 채택하는 건 아니다</p><ul><li><strong>어떤 시스템은 응답 시간과 작업 처리량 (throughput) 개선이라는 요구사항으로 인해 직접적인 동시성 구현이 불가피하다</strong></li><li>예시1) 매일 수많은 웹 사이트에서 정보를 가져와 요약하는 정보 수집기인데 단일 스레드 프로그램???<ul><li>한 번에 한 웹 사이트를 방문해 정보를 가져오고, 이 과정에서 한 사이트를 끝내야 다음 사이트로 넘어간다</li><li>웹 사이트를 계속 추가하면 정보 수집하는 시간도 늘어난다</li><li>매일 실행하므로 24시간 안에 끝나야 하지만 24시간을 넘기게 된다</li><li>단일 스레드 수집기는 웹 소켓에서 입출력을 기다리는 시간이 아주 많다</li><li>따라서 다중 스레드 알고리즘을 사용하면 수집기 성능을 높일 수 있다.</li></ul></li><li>예시 2) 한 번에 한 사용자를 처리하는 시스템: 한 사용자를 처리하는 시간은 1초<ul><li>사용자가 소수라면 시스템이 아주 빨리 반응하지만, 사용자 수가 늘어날수록 시스템이 응답하는 속도도 늦어진다</li><li>150명 뒤에 줄 서려는 사용자는 없다</li><li>대신 많은 사용자를 동시에 처리하면 시스템 응답 시간을 높일 수 있다</li></ul></li><li>예시 3) 정보를 대량으로 분석하는 시스템<ul><li>모든 정보를 처리한 후에야 최종적인 답을 낼 수 있다</li><li>정보를 나눠 여러 컴퓨터에서 돌리면 어떨까? 대량의 정보를 병렬로 처리한다면?</li></ul></li></ul><h3 id="미신과-오해"><a href="#미신과-오해" class="headerlink" title="미신과 오해"></a>미신과 오해</h3><p>동시성은 어려우며 각별히 주의하지 않으면 난감한 상황에 빠진다</p><h4 id="일반적인-오해와-미신"><a href="#일반적인-오해와-미신" class="headerlink" title="일반적인 오해와 미신"></a>일반적인 오해와 미신</h4><h5 id="동시성은-항상-성능을-높여준다"><a href="#동시성은-항상-성능을-높여준다" class="headerlink" title="동시성은 항상 성능을 높여준다?"></a>동시성은 항상 성능을 높여준다?</h5><ul><li>동시성은 항상은 아니고 때로 성능을 높여준다</li><li>대기 시간이 아주 길어 여러 스레드가 프로세서를 공유할 수 있거나, 여러 프로세서가 동시에 처리할 독립적인 계산이 충분히 많은 경우에만 성능이 높아진다</li><li>일상적으로 발생하는 상황은 아니다</li></ul><h5 id="동시성을-구현해도-설계는-변하지-않는다"><a href="#동시성을-구현해도-설계는-변하지-않는다" class="headerlink" title="동시성을 구현해도 설계는 변하지 않는다?"></a>동시성을 구현해도 설계는 변하지 않는다?</h5><ul><li>단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다</li><li>일반적으로 무엇과 언제를 분리하면 시스템 구조가 크게 달라진다</li></ul><h5 id="웹-또는-EJB-컨테이너를-사용하면-동시성을-이해할-필요가-없다"><a href="#웹-또는-EJB-컨테이너를-사용하면-동시성을-이해할-필요가-없다" class="headerlink" title="웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다?"></a>웹 또는 EJB 컨테이너를 사용하면 동시성을 이해할 필요가 없다?</h5><ul><li>실제로는 컨테이너 동작 원리와 어떻게 동시 수정(concurrent update), 데드락 등과 같은 문제를 피할 수 있는지 알아야만 한다</li></ul><h4 id="동시성과-관련된-타당한-생각들"><a href="#동시성과-관련된-타당한-생각들" class="headerlink" title="동시성과 관련된 타당한 생각들"></a>동시성과 관련된 타당한 생각들</h4><h5 id="동시성은-다소-부하를-유발한다"><a href="#동시성은-다소-부하를-유발한다" class="headerlink" title="동시성은 다소 부하를 유발한다"></a>동시성은 다소 부하를 유발한다</h5><ul><li>성능 측면에서 부하가 걸리며, 코드도 더 짜야한다 → 성능, 코드 작성 양쪽 모두 약간의 오버헤드를 일으킴</li></ul><h5 id="동시성은-복잡하다"><a href="#동시성은-복잡하다" class="headerlink" title="동시성은 복잡하다"></a>동시성은 복잡하다</h5><ul><li>간단한 문제라도 동시성은 복잡해서 해결이 쉽지 않음</li></ul><h5 id="일반적으로-동시성-버그는-재현하기-어렵다"><a href="#일반적으로-동시성-버그는-재현하기-어렵다" class="headerlink" title="일반적으로 동시성 버그는 재현하기 어렵다]"></a>일반적으로 동시성 버그는 재현하기 어렵다]</h5><ul><li>그래서 진짜 결함으로 간주되지 않고 일회성 문제(one-off)로 여겨 무시하기 쉽다</li></ul><h5 id="동시성을-구현하려면-흔히-근본적인-설계-전략을-재고해야-한다"><a href="#동시성을-구현하려면-흔히-근본적인-설계-전략을-재고해야-한다" class="headerlink" title="동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야 한다"></a>동시성을 구현하려면 흔히 근본적인 설계 전략을 재고해야 한다</h5><h2 id="난관-동시성이-구현하기-어려운-이유"><a href="#난관-동시성이-구현하기-어려운-이유" class="headerlink" title="난관 -  동시성이 구현하기 어려운 이유"></a>난관 -  동시성이 구현하기 어려운 이유</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ClassWithThreadingProblem</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> lastIdUsed;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ClassWithThreadingProblem</span><span class="params">(<span class="type">int</span> lastIdUsed)</span> {</span><br><span class="line">        <span class="built_in">this</span>.lastIdUsed = lastIdUsed;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getNextId</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> ++lastIdUsed;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String args[])</span> {</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ClassWithThreadingProblem</span> <span class="variable">classWithThreadingProblem</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassWithThreadingProblem</span>(<span class="number">42</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Runnable</span>() {</span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> {</span><br><span class="line">                classWithThreadingProblem.getNextId();</span><br><span class="line">            }</span><br><span class="line">        };</span><br><span class="line">    </span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(runnable);</span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>설명</p><ul><li>인스턴스 ClassWithThreadingProblem를 생성하고, lastIdUsed 필드를 42로 설정한 다음, 두 스레드가 해당 인스턴스를 공유한다</li><li>이제 두 스레드가 getNextId();를 호출한다고 가정하자. 결과는 셋 중 하나다.<ul><li>한 스레드는 43을 받는다. 다른 스레드는 44를 받는다. lastIdUsed는 44가 된다.</li><li>한 스레드는 44를 받는다. 다른 스레드는 43을 받는다. lastIdUsed는 44가 된다.</li><li>한 스레드는 43을 받는다. 다른 스레드는 43을 받는다. lastIdUsed는 43이 된다. (??)</li></ul></li><li>2스레드가 실행하는 잠재적 경로(N=8,T=2)는 12,870개</li><li>극히 드물지만 위와 같이 잘못된 결과가 나오게 된다</li></ul><h2 id="동시성-방어-원칙"><a href="#동시성-방어-원칙" class="headerlink" title="동시성 방어 원칙"></a>동시성 방어 원칙</h2><h3 id="단일-책임-원칙-Single-Responsibility-Principle-SRP"><a href="#단일-책임-원칙-Single-Responsibility-Principle-SRP" class="headerlink" title="단일 책임 원칙(Single Responsibility Principle, SRP)"></a>단일 책임 원칙(Single Responsibility Principle, SRP)</h3><ul><li>주어진 메서드/클래스/컴포넌트를 변경할 이유는 하나여야 한다는 원칙</li><li>동시성은 복잡성 하나만으로도 따로 분리할 이유가 충분 → 동시성 관련 코드는 다른 코드와 분리해야 한다</li><li>불행히 동시성과 관련이 없는 코드에 동시성을 곧바로 구현하는 사례가 너무도 흔함</li><li>동시성 구현시 고려사항</li></ul><ol><li>동시성 코드는 독자적인 개발, 변경, 조율 주기가 있다.</li><li>동시성 코드에는 독자적인 난관이 있다. 다른 코드에서 겪는 난관과 다르며 훨씬 어렵다.</li><li>잘못 구현한 동시성 코드는 별의별 방식으로 실패한다. 주변에 있는 다른 코드가 발목을 잡지 않더라도 동시성 하나만으로도 충분히 어렵다.<br><strong>권장사항</strong> : 동시성 코드는 다른 코드와 분리하라!</li></ol><h3 id="따름-정리-Corollary-자료-범위를-제한하라"><a href="#따름-정리-Corollary-자료-범위를-제한하라" class="headerlink" title="따름 정리(Corollary): 자료 범위를 제한하라"></a>따름 정리(Corollary): 자료 범위를 제한하라</h3><ul><li>위 예제에서 보듯이, 객체 하나를 공유한 후 동일 필드를 수정하던 두 스레드가 서로 간섭하므로 예상치 못한 결과를 내놓는다.</li><li>이런 문제를 해결하는 방안으로 공유 객체를 사용하느 코드 내 임계 영역(critical section)을 synchronized 키워드로 보호하라고 권장한다</li><li>이런 임계영역의 수를 줄이는 기술이 중요하다.</li><li>공유 자료 수정 위치가 많아지면?<ol><li>보호할 임계영역을 빼먹는다. 그래서 공유 자료를 수정하는 모든 코드를 망가뜨린다</li><li>모든 임계영역을 올바로 보호했는지(DRY 위반) 확인하느라 똑같은 노력과 수고를 반복한다</li><li>그렇지 않아도 찾아내기 어려운 버그가 더욱 찾기 어려워진다<br><strong>권장사항</strong> : 자료를 EnCapsulation하고 공유 자료를 최대한 줄여라~!</li></ol></li></ul><h3 id="따름-정리-자료-사본을-사용하라"><a href="#따름-정리-자료-사본을-사용하라" class="headerlink" title="따름 정리: 자료 사본을 사용하라"></a>따름 정리: 자료 사본을 사용하라</h3><ul><li>공유 자료를 줄이는 가장 좋은 방법? → 처음부터 공유하지 않는 방법<ul><li>어떤 경우엔 객체를 복사해 읽기 전용으로 사용하는 것이 가능</li><li>어떤 경우에는 객체의 복사본을 각 스레드에 전달, 작업을 수행하고 결과를 단일 스레드에서 수집해 사용하는 것도 가능</li></ul></li><li>공유 객체를 피하는 방법이 있다면 코드가 문제를 일으킬 가능성도 아주 낮아진다</li><li>객체의 복사에 드는 비용(시간과 부하)은?<ul><li>이 복사 비용이 정말 문제인지 실측할 필요가 있음</li><li>사본 사용으로 동기화 피할 수 있으면? → 내부 잠금이 없음 → 수행 시간 절약 → 사본생성과 가비지 컬렉션의 비용 상쇄할 가능성이 크다</li></ul></li></ul><h3 id="따름-정리-스레드는-가능한-독립적으로-구현하라"><a href="#따름-정리-스레드는-가능한-독립적으로-구현하라" class="headerlink" title="따름 정리: 스레드는 가능한 독립적으로 구현하라"></a>따름 정리: 스레드는 가능한 독립적으로 구현하라</h3><p>자신만의 세상속의 스레드를 구현한다면?</p><ul><li>다른 스레드와 자료 공유 안함</li><li>각 스레드는 클라이언트 요청 하나 처리</li><li>모든 정보는 비공유 출처에서 가져오며 로컬 변수에 저장<br>→ 다른 스레드와 동기화가 필요없음 → 각 스레드는 자신만 존재하듯 돌아갈 수 있음 → 동기화 문제 없어짐</li></ul><p>ex)HttpServlet</p><ul><li>HttpServlet의 파생 클래스는 모든 정보를 doGet, doPost 매개변수로 받아 처리</li><li>각 서블릿은 마치 자신이 독자적인 시스템에서 동작하는 것처럼 요청 처리</li><li>지역 변수를 사용하는 한 동기화 문제는 발생하지 않는다</li><li>물론 대부분의 Servlet 어플리케이션은 결국 DB 연결과 같은 자원을 공유하는 상황에 처한다<br><strong>권장사항</strong> : 독자적인 스레드로 (가능하면 다른 프로세서에서 돌려도 괜찮도록) 자료를 독립적인 단위로 분할!</li></ul><h2 id="라이브러리를-이해하라"><a href="#라이브러리를-이해하라" class="headerlink" title="라이브러리를 이해하라"></a>라이브러리를 이해하라</h2><p>자바 5부터 동시성 측면에서 이전 버전보다 많이 개선 되었음. 5이상 버전부터 스레드 구현시 고려사항</p><ul><li>자바5부터 제공하는 thread-safe 컬랙션을 사용한다</li><li>서로 연관없이 무관한 작업을 수행 시 executor 프레임워크를 사용</li><li>가능하면 스레드가 차단(block)되지 않는 논 블러킹 방법을 사용</li><li>일부 클래스 라이브러리들은 thread-safe하지 않음에 주의</li></ul><h3 id="스레드-환경에-안전한-컬렉션"><a href="#스레드-환경에-안전한-컬렉션" class="headerlink" title="스레드 환경에 안전한 컬렉션"></a>스레드 환경에 안전한 컬렉션</h3><p><code>java.util.concurrent</code></p><ul><li>자바 초창기 : Doug Lea, Concurrent Programming in Java</li><li>해당 책에서 스레드에서 안전한 컬렉션 몇 개를 구현 했는데 나중에 자바 패키지에 추가됨</li><li>해당 패키지의 클래스는 다중 스레드에서 안정되며, 성능도 좋음</li><li><code>ConcurrentHashMap</code>은 실제로 거의 모든 상황에서 HashMap보다 빠르다<ul><li>동시 읽기/쓰기 지원</li><li>(보통 다중 스레드에서 문제가 생기는) 자주 사용하는 복합 연산을 안전하게 만든 메서드로 제공</li></ul></li></ul><p>좀 더 복잡한 동시성 설계를 지원하는 클래스 몇 가지</p><table><thead><tr><th align="left">Name</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">ReentrantLock</td><td align="left">한 메서드에서 잠그고 다른 메서드에서 풀 수 있는 lock이다.</td></tr><tr><td align="left">Semaphore</td><td align="left">전통적인 세마포어(갯수를 셀 수 있는 lock)의 구현체.</td></tr><tr><td align="left">CountDownLatch</td><td align="left">지정한 특정 횟수의 이벤트가 발생하고 나서야 기다리는 모든 스레드들을 해제 할 수 있는 lock이다. 모든 스레드가 거의 동시에 시작될 수 있는 기회를 준다</td></tr><tr><td align="left"><strong>권장사항</strong> :</td><td align="left"></td></tr></tbody></table><ul><li>언어가 제공하는 클래스를 검토하라. 자바의 경우 다음을 익혀라<ul><li><code>java.util.concurrent</code></li><li><code>java.util.concurrent.atomic</code></li><li><code>java.util.concurrent.locks</code></li></ul></li></ul><h2 id="실행-모델을-이해하라"><a href="#실행-모델을-이해하라" class="headerlink" title="실행 모델을 이해하라"></a>실행 모델을 이해하라</h2><p>기본용어</p><table><thead><tr><th align="left">Name</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">Bound Resources(한정된 자원)</td><td align="left">Concurrent 환경에서 사용되는 고정된 크기의 자원이다. 예시로 데이터베이스 연결, 고정된 크기의 읽기/쓰기 버퍼가 있다.</td></tr><tr><td align="left">Mutual Exclusion(상호 배제)</td><td align="left">한 번에 한 스레드만 공유 자료/공유 자원을 사용할 수 있는 경우</td></tr><tr><td align="left">Starvation(기아)</td><td align="left">한 스레드 or 스레드 그룹이 긴 시간 or 영원히 자원 대기. 예를 들어, 항상 짧은 스레드에게 우선 순위를 준다면,  수행 시간이 긴 스레드는 굶게 된다(기아 상태).</td></tr><tr><td align="left">Deadlock(데드락)</td><td align="left">여러 스레드가 서로가 끝나기를 기다린다. 각 스레드는 서로가 필요로 하는 자원을 점유하고 있기 떄문에 어느 쪽도 더 이상 진행하지 못함</td></tr><tr><td align="left">Livelock(라이브락)</td><td align="left">락을 거는 단계에서 각 스레드가 서로를 방해. 스레드들이 서로 작업을 수행하려는 중 다른 스레드가 작업중인 것을 인지하고 서로 양보한다. 이러한 공명(resonance) 때문에 스레드들은 작업을 계속 수행하려 하지만 장시간 혹은 영원히 작업을 수행하지 못하게 된다.</td></tr></tbody></table><p>기본 개념을 이해했으면 다중 스레드 프로그래밍에서 사용하는 실행 모델을 살펴본다 </p><h3 id="생산자-소비자-Producer-Consumer"><a href="#생산자-소비자-Producer-Consumer" class="headerlink" title="생산자-소비자(Producer-Consumer)"></a>생산자-소비자(Producer-Consumer)</h3><ul><li>한 개 이상의 생산자 스레드가 정보를 생성한 후 버퍼나 큐에 넣는다</li><li>한 개 이상의 소비자 스레드가 버퍼 혹은 큐에서 정보를 가져와 사용</li><li>생산자와 소비자 사이에 있는 큐는 bound resource(한정된 자원)이다</li><li>따라서 생산자는 큐에 빈 공간이 생길 때까지, 소비자는 큐에 정보가 하나라도 생길 때까지 기다린다</li><li>대기열을 올바로 사용하기 위해 생산자/소비자는 서로에게 시그널을 보낸다</li><li>생산자는 큐에 작업물을 넣고 소비자에게 “큐가 비어있지 않다”는 신호를 보내고<br>소비자는 큐에서 작업물을 꺼낸 후 “큐가 가득차 있지 않다”는 신호를 보낸다 그 전까지 둘은 신호를 기다린다.</li><li>따라서 잘못하면 생산자/소비자 둘 다 진행 가능함에도 서로 시그널을 기다릴 가능성이 존재</li></ul><h3 id="읽기-쓰기-Readers-Writers"><a href="#읽기-쓰기-Readers-Writers" class="headerlink" title="읽기-쓰기(Readers-Writers)"></a>읽기-쓰기(Readers-Writers)</h3><p>예) 읽기 스레드를 위한 주된 정보원으로 사용되는 공유자원인데, 쓰기 스레드에 의해 이따금씩 갱신되는 경우<br>예시의 경우 처리율(throughput)이 핵심 포인트</p><ul><li>쓰기가 갱신 중에 읽기 스레드가 멈추고, 읽기 스레드가 읽는 중엔 쓰기 스레드가 갱신을 멈춰야 한다</li><li>처리율 강조? 독자가 상대적 우선권 → starvation(기아) 현상 or 오래되고 정체된 정보로 가득</li><li>갱신 허용? : 쓰기가 상대적 우선권 → 처리율이 감소</li></ul><p>간단한 해법 : 읽기 스레드가 없을 때까지 갱신을 원하는 쓰기 스레드가 버퍼를 기다리는 방법</p><ul><li>읽기 스레드 계속? 쓰기가 기아 상태에 빠짐</li><li>쓰기 스레드 계속? 처리율이 떨어짐</li></ul><p>양쪽 균형을 잡으면서 동시 갱신 문제를 피하는 해법이 필요</p><h3 id="식사하는-철학자들-Dining-Philosophers"><a href="#식사하는-철학자들-Dining-Philosophers" class="headerlink" title="식사하는 철학자들(Dining Philosophers)"></a>식사하는 철학자들(Dining Philosophers)</h3><p>예시</p><ul><li>원탁을 둘러싼 여러 명의 철학자들</li><li>각 철학자의 왼쪽에 포크가 놓여 있으며 테이블의 중앙에 큰 스파게티 한 그릇이 놓여 있다</li><li>그들은 배가 고파지기 전까지 각자 생각을 하며 시간을 보낸다</li><li>배가 고파지면 그들은 자신의 양쪽에 놓여 있는 포크 2개를 잡고 스파게티를 먹는다</li><li>철학자는 포크 2개가 있어야만 스파게티를 먹을 수 있다.</li><li>그렇지 않다면 옆 사람이 포크를 다 사용하기 전까지 기다려야 한다.</li><li>스파게티를 먹은 철학자는 다시 배가 고파질 때까지 포크를 놓고 있게 된다.</li><li>위 상황에서 철학자를 스레드로, 포크를 공유 자원으로 바꾸게 되면?<br>이는 자원을 놓고 경쟁하는 프로세스와 비슷한 상황</li><li>잘 설계되지 않은 시스템은 deadlock, livelock, 처리량 문제, 효율성 저하 문제에 맞닥뜨리기 쉽다</li></ul><p>일상에서 접하는 대부분의 concurrent관련 문제들은 이 세 범주 중 하나에 속한다</p><ul><li>이 알고리즘들을 공부하고 스스로 해법을 직접 구현해보라</li><li>그러면 나중에 실전 문제에서 해결이 쉬어진다<br><strong>권장 사항</strong> : 위에서 설명한 기본 알고리즘과 각 해법을 이해하라</li></ul><h2 id="동기화하는-메서드-사이에-존재하는-의존성을-이해하라"><a href="#동기화하는-메서드-사이에-존재하는-의존성을-이해하라" class="headerlink" title="동기화하는 메서드 사이에 존재하는 의존성을 이해하라"></a>동기화하는 메서드 사이에 존재하는 의존성을 이해하라</h2><ul><li>동기화된 메서드 사이에 의존성이 존재하면 동시성 코드에서 찾아내기 어려운 버그가 생김</li><li>자바는 synchronized라는 개별 메서드 보호 개념 지원</li><li>하지만 한 공유 클래스에 두 개 이상의 synchronized 메서드가 존재하면 문제를 일으킬 수도 있다.</li></ul><p><strong>권장 사항</strong> : 공유 객체 하나에는 메서드 하나만 사용</p><p>권장사항 밖의 상황 : 공유 객체 하나에 여러 메서드가 필요한 경우?</p><h4 id="Client-Based-Locking-클라이언트-기반-잠금"><a href="#Client-Based-Locking-클라이언트-기반-잠금" class="headerlink" title="Client-Based Locking(클라이언트 기반 잠금)"></a>Client-Based Locking(클라이언트 기반 잠금)</h4><ul><li>클라이언트에서 첫 번째 메서드를 호출하기 전에 서버를 잠근다</li><li>(공유 객체를 사용하는 코드에서 공유 객체 Lock)</li><li>마지막 메서드를 호출할 떄까지 잠금 유지</li><li>Bad<ul><li>서버를 사용하는 모든 클라이언트 코드에서 lock이 필요</li><li>이는 유지보수 및 디버깅에 필요한 비용을 상승시킨다.</li></ul></li></ul><h4 id="Server-Based-Locking"><a href="#Server-Based-Locking" class="headerlink" title="Server-Based Locking"></a>Server-Based Locking</h4><ul><li>서버 에다 “서버(자신)을 잠그고 모든 동작을 수행한 후 잠금을 푸는” 메서드를 구현</li><li>클라이언트는 이 메서드를 호출</li><li>(공유 객체에 새로운 메서드를 작성하고 잠금이 필요한 동작 전체를 수행하게 하는 것)<br>=&gt; Good: Critical section에 접근하는 코드를 최소화</li></ul><h4 id="Adapted-Server"><a href="#Adapted-Server" class="headerlink" title="Adapted Server"></a>Adapted Server</h4><ul><li>잠금을 수행하는 중계자(중간 단계)를 생성</li><li>‘서버에서 잠금’ 방식과 유사하나 원래 서버는 변경하지 않음</li><li>이는 기본적으로 서버 기반 잠금이지만 기존의 서버를 변경할 수 없는 상황에 사용할 수 있는 방법</li><li>(서드 파티 라이브러리를 사용한다고 생각하면 쉬울 것이다)<br>=&gt; Good: 서버 기반 잠금 방식을 사용할 수 없는 경우에 사용</li></ul><h3 id="동기화하는-부분을-작게-만들어라"><a href="#동기화하는-부분을-작게-만들어라" class="headerlink" title="동기화하는 부분을 작게 만들어라"></a>동기화하는 부분을 작게 만들어라</h3><ul><li>자바의 Synchronized 키워드 사용 → 락 설정</li><li>같은 락으로 감싼 모든 코드 영역은 한 번에 한 스레드만 실행 가능</li><li>락은 스레드 지연(delay)과 부하 가중 유발(비싼 수행) →  Synchronized 남발하면 안된다</li><li>반면 critical section은 꼭 보호되어야 한다.<ul><li>임계 영역 : 동시 사용을 막아야만 프로그램이 올바로 동작하는 보호받는 코드 영역</li></ul></li><li>코드를 짤 때 임계 영역수를 최대한 줄여야 한다<ul><li>순진한 프로그래머 : 임계 영역 수를 줄이기 위해 거대한 임계 영역 하나로 구현</li><li>필요이상의 임계영역 크기 → 스레드 간에 경쟁이 늘어나고 프로그램 성능 감소<br><strong>권장 사항</strong> : 동기화된 영역은 최대한 작게 만들어라.</li></ul></li></ul><h3 id="올바른-종료-코드는-구현하기-어렵다"><a href="#올바른-종료-코드는-구현하기-어렵다" class="headerlink" title="올바른 종료 코드는 구현하기 어렵다"></a>올바른 종료 코드는 구현하기 어렵다</h3><ul><li>“영구적으로 돌아가는 시스템 구현”과 “잠시 돌다 깔끔하게 종료하는 시스템 구현”은 방법이 다르다</li><li>깔끔하게 종료하는 코드 : 올바로 구현이 어려움<ul><li>가장 흔한 문제 : 데드락(스레드가 절대 오지 않을 시그널 기다림)</li><li>예1) 부모 스레드가 자식 스레드를 여러개 만든 후 다 끝나기를 기다렸다가 자원을 해제하고 종료하는 시스템<ul><li>자식 스레드 하나가 데드락에 걸리면?</li><li>부모 스레드는 영원히 가디라고, 시스템은 영원히 종료하지 못한다</li></ul></li><li>예2) 유사 시스템의 종료 지시를 받고 부모가 모든 자식 스레드에게 종료 시그널 전달<ul><li>자식 스레드 중 2개가 생산자/소비자 관계인데 생산자가 재빨리 종료되고 소비자가 waiting이라면?</li><li>생산자 메시지를 waiting하는 소비자는 blocked 상태 → 종료시그널 못받음</li><li>소비자는 영원히 생산자 스레드 기다림 → 부모는 자식 스레드를 영원히 기다림</li></ul></li><li>종종 발생하는 상황임</li></ul></li><li>따라서 깔끔하게 종료하는 멀티 스레드 코드 작성하려면 시간을 투자해 올바로 구현해야 한다<br><strong>권장 사항</strong></li><li>종료 코드를 개발 초기부터 고민하고 동작하게 초기부터 구현하라</li><li>생각보다 오래 걸린다</li><li>생각보다 어려우므로 이미 나온 알고리즘을 검토하라</li></ul><h3 id="스레드-코드-테스트하기"><a href="#스레드-코드-테스트하기" class="headerlink" title="스레드 코드 테스트하기"></a>스레드 코드 테스트하기</h3><p>스레드 1개에서 무조건 옳은 경우</p><ul><li>코드가 올바르다고 증명은 현실적으로 불가능</li><li>테스트는 정확성을 보장하지 않으며 “코드가 제대로 작성되었는가”를 증명할 수 없다</li><li>다만 잘 작성된 충분한 테스트는 위험을 최소화할 수 있다</li><li>이는 같은 코드와 같은 자원을 사용하는 멀티 스레드 상황에서는 훨씬 더 복잡해 진다</li></ul><p><strong>권장 사항</strong></p><ul><li>문제를 노출할 만한 테스트 케이스를 작성하라</li><li>프로그램 설정, 시스템 설정, 부하를 바꿔가며 자주 수행하라</li><li>테스트가 한 번이라도 실패하면 원인을 분석하라</li><li>다시 돌렸더니 통과하더라는 이유로 넘어가면 절대 안된다</li></ul><p>멀티 스레드 테스트 지침</p><ul><li>말이 안 되는 실패는 잠정적인 스레드 문제로 취급하라</li><li>다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자</li><li>다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있게 스레드 코드를 구현하라</li><li>다중 스레드를 쓰는 코드 부분을 상황에 맞게 조율할 수 있게 작성하라</li><li>프로세서 수보다 많은 스레드를 돌려보라</li><li>다른 플랫폼에서 돌려보라</li><li>코드에 보조 코드instrument를 넣어 돌려라. 강제로 실패를 일으키게 해보라</li></ul><h4 id="말이-안-되는-실패는-잠정적인-스레드-문제로-취급하라"><a href="#말이-안-되는-실패는-잠정적인-스레드-문제로-취급하라" class="headerlink" title="말이 안 되는 실패는 잠정적인 스레드 문제로 취급하라"></a>말이 안 되는 실패는 잠정적인 스레드 문제로 취급하라</h4><ul><li>멀티 스레드 코드는 일반적으로 발생할 리 없어 보이는 문제를 발생</li><li>(저자를 포함한)대부분의 개발자는 이러한 문제를 직관적으로 이해 불가</li><li>스레드 코드의 코드는 파악하지 않으면서 때로는 수백만 번에 한번씩  매우 드물게 발생하기도 함</li><li>그래서 많은 개발자들은 이러한 문제들을 우주선(宇宙線), 하드웨어 버그, 혹은 이러한 류의 one-off로 치부</li><li>제일 안정된 방향은 one-off(일회성) 문제는 없다고 판단하는 것</li><li>이러한 one-off들이 무시될 수록 더 많은 코드들이 이미 문제가 있는 시스템에 추가되게 될 뿐</li></ul><p><strong>권장 사항</strong> : 시스템 오작동을 일회성으로 치부하지 마라</p><h4 id="다중-스레드를-고려하지-않은-순차-코드부터-제대로-돌게-만들자"><a href="#다중-스레드를-고려하지-않은-순차-코드부터-제대로-돌게-만들자" class="headerlink" title="다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자"></a>다중 스레드를 고려하지 않은 순차 코드부터 제대로 돌게 만들자</h4><ul><li>당연한 소리, 스레드 환경 밖에서 코드가 제대로 동작하는지 반드시 확인</li><li>일반적으로 스레드가 호출하는 POJO작성→POJO는 스레드를 모름→스레드 환경 밖에서 테스트 가능</li><li>따라서 POJO에 넣는 코드가 많을 수록 더 좋다<br><strong>권장 사항</strong> : 먼저 스레드 환경 밖에서 코드를 올바로 돌려라<br>→스레드 환경 밖의 버그와 스레드 환경하에서 생기는 버그를 동시에 디버깅하려 하지 말라</li></ul><h4 id="다중-스레드를-쓰는-코드-부분을-다양한-환경에-쉽게-끼워-넣을-수-있게-스레드-코드를-구현하라"><a href="#다중-스레드를-쓰는-코드-부분을-다양한-환경에-쉽게-끼워-넣을-수-있게-스레드-코드를-구현하라" class="headerlink" title="다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있게 스레드 코드를 구현하라"></a>다중 스레드를 쓰는 코드 부분을 다양한 환경에 쉽게 끼워 넣을 수 있게 스레드 코드를 구현하라</h4><ul><li>한 스레드로 실행, 여러 스레드로 실행, 실행 중 스레드 수 바꿔보기</li><li>스레드 코드를 실제 환경이나 테스트 환경에서 돌려보기</li><li>빨리, 천천히 등 다양한 속도로 테스트를 돌려보기</li><li>반복 테스트가 가능하도록 테스트 케이스 작성하기<br><strong>권장 사항</strong> : 다양한 설정에서 실행할 목적으로 다른 환경에 쉽게 끼워 넣을 수 있게 코드를 구현하라</li></ul><h4 id="다중-스레드를-쓰는-코드-부분을-상황에-맞게-조율할-수-있게-작성하라"><a href="#다중-스레드를-쓰는-코드-부분을-상황에-맞게-조율할-수-있게-작성하라" class="headerlink" title="다중 스레드를 쓰는 코드 부분을 상황에 맞게 조율할 수 있게 작성하라"></a>다중 스레드를 쓰는 코드 부분을 상황에 맞게 조율할 수 있게 작성하라</h4><ul><li>적절한 스레드 개수 파악은 상당한 시행착오를 필요</li><li>처음부터 다양한 설정으로 프로그램 성능 측정 방법 강구</li><li>실행할 스레드 갯수를 쉽게 변경할 수 있게 작성</li><li>시스템이 동작하는 도중에 스레드 개수를 변경할 수 있게 하는 것을 고려한다</li><li>처리량과 시스템 활용도를 기준으로 스스로를 조정할 수 있게 하는 것을  고민한다</li></ul><h4 id="프로세서-수보다-많은-스레드를-돌려보라"><a href="#프로세서-수보다-많은-스레드를-돌려보라" class="headerlink" title="프로세서 수보다 많은 스레드를 돌려보라"></a>프로세서 수보다 많은 스레드를 돌려보라</h4><ul><li>시스템이 스레드 스와핑 할 때돟 문제는 발생</li><li>작업 전환을 빈번히 발생하게 하기 위해 프로세서 수보다 많은 스레드를 실행해 보라</li><li>작업 전환이 잦을수록 빠뜨린 critical section이나 dead lock을 찾을 확률이 높아지게 된다</li></ul><h4 id="다른-플랫폼에서-돌려보라"><a href="#다른-플랫폼에서-돌려보라" class="headerlink" title="다른 플랫폼에서 돌려보라"></a>다른 플랫폼에서 돌려보라</h4><ul><li>멀티 스레드 코드는 실행 환경에 따라 다르게 동작</li><li>따라서 모든 잠재적 배포 환경에 대해 테스트를 수행해야 한다.<br><strong>권장 사항</strong> : 처음부터, 그리고 자주, 모든 타겟 플랫폼에서 코드를 돌려라</li></ul><h4 id="코드에-보조-코드instrument를-넣어-돌려라-강제로-실패를-일으키게-해보라"><a href="#코드에-보조-코드instrument를-넣어-돌려라-강제로-실패를-일으키게-해보라" class="headerlink" title="코드에 보조 코드instrument를 넣어 돌려라. 강제로 실패를 일으키게 해보라"></a>코드에 보조 코드instrument를 넣어 돌려라. 강제로 실패를 일으키게 해보라</h4><p>스레드 버그는 어려움</p><ul><li>눈으로 보이지 않고 간단한 테스트로 버그 발견 안됨</li><li>코드가 실행되는 수천 가지 경로 중에 아주 소수만 실패</li><li>오류를 자주 일으킬 수 있는 방법 : 보조 코드 추가 ▶ 직접 구현하기, 자동화 이렇게 2가지 방법</li></ul><h3 id="직접-구현하기"><a href="#직접-구현하기" class="headerlink" title="직접 구현하기"></a>직접 구현하기</h3><blockquote><p>Object.wait(), Object.sleep(), Object.yield(), Object.priority()등의 메서드를 사용해 실행 경로를 변경함으로써 코드의 문제를 발견하는 방법</p></blockquote><p>sleep</p><ul><li>지정한 시간동안 작업을 일시정지 상태로 들어가는 메서드</li><li>static 메서드로 제공되며(인스턴스 메서드는 deprecated 되었다),</li><li>이 메서드를 호출한 쓰레드가 지정한 시간만큼 일시정시 상태가 된다</li></ul><p>sleep vs wait</p><ul><li>sleep 은 Thread클래스, wait는 object 클래스</li><li>sleep은 객체가 중단되거나 실행 완료까지 객체 락 유지, wait은 락해제, 다른객체 notify시 다시 runnable</li></ul><p>yield</p><ul><li>RUNNABLE 상태로 들어가면서 다른 쓰레드에게 작업을 양보하는 메서드이다</li><li>이 또한 static 메서드로 제공되며, 이 메서드를 호출한 쓰레드가 RUNNABLE 상태로 들어가게 된다<figure class="highlight java"><figcaption><span>테스트를 위해 yield 추가</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> String <span class="title function_">nextUrlOrNull</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">if</span>(hasNext()) {</span><br><span class="line">        <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> urlGenerator.next();</span><br><span class="line">        Thread.yield(); <span class="comment">// inserted for testing</span></span><br><span class="line">        updateHasNext();</span><br><span class="line">        <span class="keyword">return</span> url;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>설명</li><li>yield() 메서드를 호출함으로써 코드의 실행 경로를 변경 가능</li><li>위 코드에서 문제가 발생한다면 이는 yield()를 추가해 생긴 문제가 아니라 이미 존재하던 문제를 명백히 만든것 뿐이다.<br>문제점</li><li>보조 코드를 삽입할 적정 위치를 직접 찾아야 한다.</li><li>어떤 메서드를 어디서 호출해야 좋은지 어떻게 알 것인가</li><li>제품에 이와 같은 보조 코드를 포함해 배포하는 것은 불필요하게 퍼포먼스를 저하</li><li>Shotgun approach 이기 때문에(무작위) 반드시 문제가 발생한다는 보장을 얻을 수 없다. 사실상 문제가 드러나지 않을 확율이 더 높다<br>(Shotgun approach: 산탄총처럼 되는대로 시도하는 방법)<br>→ 배포 환경이 아니라 테스트 환경에서 설정을 바꿔가며 보조코드를 실행할 방법이 필요</li></ul><p>시스템을 최대한 POJO와 스레드 제어 클래스로 분할해야한다</p><ul><li>보조코드를 추가할 위치를 찾기가 쉬어진다</li><li>여러 상황에서 sleep, yield등으로 POJO를 호출하게 다양한 jig 구현 가능</li></ul><h3 id="자동화"><a href="#자동화" class="headerlink" title="자동화"></a>자동화</h3><p>보조 코드 자동 추가 : AOF(Aspect-Oriented Framework), CGLIB, ASM과 같은 도구</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 메서드가 하나인 클래스</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadJigglePoint</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">jiggle</span><span class="params">()</span> { }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//다양한 위치에 ThreadJiglePoint.jiggle() 추가 : 무작위 sleep, yield 호출 or nop</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> String <span class="title function_">nextUrlOrNull</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">if</span>(hasNext()) {</span><br><span class="line">        ThreadJiglePoint.jiggle();</span><br><span class="line">        <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> urlGenerator.next();</span><br><span class="line">        ThreadJiglePoint.jiggle();</span><br><span class="line">        updateHasNext();</span><br><span class="line">        ThreadJiglePoint.jiggle();</span><br><span class="line">        <span class="keyword">return</span> url;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>ThreadJiglePoint를 2가지로 구현</p><ol><li><code>jiggle()</code>을 비워두고 배포 환경에서 사용</li><li>무작위로 nop, sleep, yield등을 테스트 환경에서 수행</li></ol><ul><li>2번 구현으로 테스트를 수천 번 반복하면 스레드 오류가 나타날 지도</li><li>단순하나 복잡한 도구 사용이 어렵다면 합리적 대안으로 나쁘지 않음</li><li>IBM의 ConTest : 유사 작동하나 좀 더 복잡</li><li>코드 흔들기(jiggle)→스레드 다른 순서 실행→ 스레드 오류 드러날 확율 높여줌<br><strong>권장 사항</strong> : 흔들기 기법을 사용해 오류를 찾아라</li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li><p>다중 스레드 코드는 제대로 작성하기 어렵다</p></li><li><p>다중 스레드 코드는 엄격한 기준으로 clean하게 작성하라</p></li><li><p>최우선적으로 SRP를 준수하라 : POJO를 사용 스레드 관련 코드와 非스레드 관련 코드를 분리하라</p></li><li><p>스레드 관련 코드를 테스트할 때에는 전적으로 스레드 관련 문제만 테스트하라</p></li><li><p>즉, 스레드 코드는 최대한 집약되고 작아야 한다</p></li></ul><p>동시성 오류를 일으키는 잠정적 원인을 철저이 이해한다</p><ul><li><p>여러 스레드가 공유 자료 조작 or 자원 풀 공유시 동시성 오류 발생</p></li><li><p>깔끔하게 종료되게 하는 문제나 반복문 탈출과 같은 경계 조건의 경우는 특히 주의</p></li><li><p>사용하는 라이브러리를 이해하고 기본적인 알고리즘을 이해하라</p></li><li><p>라이브러리가 제공하는 기능이 어떻게 문제를 해결하는지 이해하라</p></li></ul><p>보호할 코드 영역을 찾는 법, 특정 코드 영역을 잠그는 방법을 이해한다</p><ul><li>잠글 필요가 없는 코드는 잠그지 마라</li><li>잠긴 영역에서 다른 잠긴 영역 호출 X<br>그러려면? → 공유하는 정보와 공유하지 않는 정보를 제대로 이해 필요</li><li>공유 객체의 갯수와 공유 범위를 최소한으로 줄여라</li><li>클라이언트에게 공유 객체의 상태(잠금 등)를 관리하는 책임을 떠넘기지 않는다<br>필요시 객체 디자인 변경으로 클라이언트에 편의를 제공</li></ul><p>스레드 관련 코드는 여러 설정, 환경에서 반복적이고 지속적으로 수행해야 한다</p><ul><li>버그를 일회성으로 치부하지 않고 발견해서 고칠 수 있도록</li></ul><p>테스트 용이성</p><ul><li>TDD 3대규칙을 따르면 자연적으로 얻어짐</li><li>좀더 넓은 설정 범위에서 코드를 수행하기 위해 필요한 기능을 제공하는 플러그인 수준을 의미</li></ul><p>보조코드 추가</p><ul><li>코드를 시간을 들여 instrument하게 되면 오류가 드러날 확률은 크게 높아진다</li><li>직접 코드를 작성할 수도 있고 자동화 툴을 사용할 수도 있다</li><li>초반부터 보조 코드 고려</li><li>스레드 코드는 출시하기 전까지 최대한 오래 테스트 해야할 것</li><li>Clean한 접근 방식을 사용한다면, 코드가 올바로 돌아갈 가능성이 극적으로 높아진다</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch13/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅻ. 창발성(創發性)</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch12/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch12/</guid>
      <pubDate>Wed, 09 Feb 2022 00:55:43 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅻ-창발성-創發性&quot;&gt;&lt;a href=&quot;#Ⅻ-창발성-創發性&quot; class=&quot;headerlink&quot; title=&quot;Ⅻ. 창발성(創發性)&quot;&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅻ-창발성-創發性"><a href="#Ⅻ-창발성-創發性" class="headerlink" title="Ⅻ. 창발성(創發性)"></a>Ⅻ. 창발성(創發性)</h1><blockquote><ul><li>비로소 창, 쏠 발, 성품 성</li><li>불시에 솟아 나는 특성, emergent property, emergence</li><li>단순한 결합이 복잡한 결과를 나타내는 것을 의미<ul><li>예) 인간의 뇌 : 하나의 뉴런은 인식 능력이 없으나 수십억개가 결합하면 자기 인식 발생</li></ul></li><li>명령을 내리는 조정자 없이 각 부분의 의사소통으로 자기 조직화를 이루게 되고<br>  이러한 밑으로 부터의 힘은 예기치 못한 기능을 발현하는 힘 Ex) 집단 지성</li><li>창발적 설계 : 어떤 규칙과 원칙에 따라 설계를 하게 되면, 그것들이 모여 아주 좋은 거시적 설계가 된다는 원리?</li></ul></blockquote><h2 id="창발적-설계로-깔끔한-코드를-구현하자"><a href="#창발적-설계로-깔끔한-코드를-구현하자" class="headerlink" title="창발적 설계로 깔끔한 코드를 구현하자"></a>창발적 설계로 깔끔한 코드를 구현하자</h2><h4 id="단순한-설계-규칙-4가지-4-Rules-of-Simple-Design"><a href="#단순한-설계-규칙-4가지-4-Rules-of-Simple-Design" class="headerlink" title="단순한 설계 규칙 4가지(4 Rules of Simple Design)"></a>단순한 설계 규칙 4가지(4 Rules of Simple Design)</h4><ul><li>켄트백이 90년에 “Extreme Programming Explained”에서 설명</li><li>착실하게 따르기만 하면<ul><li>우수한 설계가 나오며</li><li>코드 구조와 설계 파악이 쉬워지고</li><li>따라서 SRP, DIP같은 원칙 적용이 쉬워지며</li><li>우수한 설계의 창발성을 촉진한다</li></ul></li><li>규칙 내용 : 이를 따르면 Design is Simple하다!(..라고 켄트백이 말했다;) 중요도 순서로 나열<ol><li>모든 테스트를 실행한다</li><li>중복을 없앤다</li><li>프로그래머 의도를 표현한다</li><li>클래스와 메서드 수를 최소로 줄인다</li></ol></li></ul><h2 id="단순한-설계-규칙-1-모든-테스트를-실행하라"><a href="#단순한-설계-규칙-1-모든-테스트를-실행하라" class="headerlink" title="단순한 설계 규칙 1: 모든 테스트를 실행하라"></a>단순한 설계 규칙 1: 모든 테스트를 실행하라</h2><h5 id="First-설계는-의도한-대로-돌아가는-시스템을-내놓아야"><a href="#First-설계는-의도한-대로-돌아가는-시스템을-내놓아야" class="headerlink" title="First, 설계는 의도한 대로 돌아가는 시스템을 내놓아야"></a>First, 설계는 의도한 대로 돌아가는 시스템을 내놓아야</h5><ul><li>문서로는 완벽히 설계했지만, 시스템이 의도한 대로 돌아가는지 검증할 간단한 방법이 없다면?</li><li>문서 작성을 위해 투자한 노력에 대한 가치는 인정받기 힘들다.</li></ul><h5 id="테스트가-불가능한-시스템은-검증도-불가능"><a href="#테스트가-불가능한-시스템은-검증도-불가능" class="headerlink" title="테스트가 불가능한 시스템은 검증도 불가능"></a>테스트가 불가능한 시스템은 검증도 불가능</h5><ul><li>테스트를 철저히 거쳐 모든 테스트 케이스를 항상 통과하는 시스템은 ‘테스트가 가능한 시스템’</li><li>테스트가 불가능한 시스템은 검증도 불가능하다</li><li>논란의 여지는 있지만, 검증이 불가능한 시스템은 절대 출시하면 안 된다</li></ul><h5 id="철저한-테스트가-가능한-시스템-지향-→-더-좋은-설계가-결과로"><a href="#철저한-테스트가-가능한-시스템-지향-→-더-좋은-설계가-결과로" class="headerlink" title="철저한 테스트가 가능한 시스템 지향 → 더 좋은 설계가 결과로"></a>철저한 테스트가 가능한 시스템 지향 → 더 좋은 설계가 결과로</h5><ul><li>다행스럽게도, 테스트가 가능한 시스템을 만들려고 애쓰면 설계 품질이 더불어 높아진다<ul><li>크기가 작고 목적 하나만 수행하는 클래스가 나온다</li><li>SRP를 준수하는 클래스는 테스트가 훨씬 더 쉽다</li><li>테스트를 더 많이 작성하면 할수록 프로그래머가 더 테스트하기 간단하게 코드를 작성할 수 있게 도와준다</li></ul></li><li>따라서 철저한 테스트가 가능한 시스템을 만들면 더 나은 설계가 얻어진다</li><li><strong>자동으로 결합도도 낮아짐</strong><ul><li>결합도가 높으면 테스트 케이스를 작성하기 어렵다. 따라서 지금까지와 마찬가지로</li><li>테스트 케이스를 많이 작성할수록 개발자는 DIP와 같은 원칙을 적용하고 의존성 주입(Dependency Injection),인터페이스, 추상화 등과 같은 도구를 사용해 결합도를 낮춘다</li><li>따라서 설계 품질은 더욱 높아진다</li></ul></li><li>결론<ul><li>놀랍게도 “테스트 케이스를 만들고 계속 돌려라”라는 간단하고 단순한 규칙을 따르면</li><li>시스템은 낮은 결합도와 높은 응집력이라는, 객체 지향 방법론이 지향하는 목표를 저절로 달성한다</li><li>즉, 테스트 케이스를 작성하면 설계 품질이 높아진다.</li></ul></li></ul><h2 id="단순한-설계-규칙-2-4-리팩터링"><a href="#단순한-설계-규칙-2-4-리팩터링" class="headerlink" title="단순한 설계 규칙 2~4: 리팩터링"></a>단순한 설계 규칙 2~4: 리팩터링</h2><h5 id="걱정없이-리팩터링"><a href="#걱정없이-리팩터링" class="headerlink" title="걱정없이 리팩터링~"></a>걱정없이 리팩터링~</h5><ul><li>테스트 케이스를 모두 작성했다면 이제 코드와 클래스를 정리해도 괜찮다</li><li>구체적으로는 코드를 점진적으로 리팩터링 해나간다<ul><li>코드 몇 줄을 추가할 때마다 잠시 멈추고 설계를 조감한다</li></ul></li><li>새로 추가하는 코드가 설계 품질을 낮춘다면??<ul><li>깔끔히 정리한 후 테스트를 돌려 기존 기능을 깨뜨리지 않았다는 사실을 확인</li></ul></li><li>코드를 정리하면서 시스템이 깨질까 걱정할 필요가 없다. 테스트 케이스가 있으니까!</li></ul><h5 id="sw-설계-품질-기법-적용"><a href="#sw-설계-품질-기법-적용" class="headerlink" title="sw 설계 품질 기법 적용"></a>sw 설계 품질 기법 적용</h5><ul><li>리팩터링 단계에서는 소프트웨어 설계 품질을 높이는 기법이라면 무엇이든 적용해도 괜찮다</li><li>응집도를 높이고, 결합도를 낮추고, 관심사를 분리하고, 시스템 관심사를 모듈로 나누고, 함수와 클래스 크기를 줄이고, 더 나은 이름을 선택하는 등 다양한 기법을 동원한다</li><li>또한 이 단계는 단순한 설계 규칙 중 나머지 3개를 적용해서 <ul><li>중복 제거</li><li>프로그래머 의도 표현</li><li>클래스 메서드를 최소한으로 줄인다</li></ul></li></ul><h2 id="중복을-없애라"><a href="#중복을-없애라" class="headerlink" title="중복을 없애라"></a>중복을 없애라</h2><ul><li>우수한 설계에서 중복은 커다란 적이다</li><li>중복은 추가 작업, 추가 위험, 불필요한 복잡도를 뜻하기 때문</li><li>중복은 여러 가지 형태로 표출된다<ul><li>똑같은 코드 → 당연히 중복</li><li>비슷한 코드 → 더 비슷하게 고쳐주면 리팩터링이 쉬워진다</li><li>구현 중복도 중복의 한 형태<figure class="highlight java"><figcaption><span>집합 클래스에 다음 메서드가 있을 때</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> {}</span><br><span class="line"><span class="type">boolean</span> isEmpty{}</span><br></pre></td></tr></tbody></table></figure>구현</li></ul></li><li>각 메서드를 따로 구현하는 방법도 있다</li><li>하지만 size()가 개수를 반환하는 로직</li><li>isEmpty가 이를 이용하면 중복 구현할 필요 없음<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> {</span><br><span class="line">  <span class="keyword">return</span> <span class="number">0</span> == size();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><p>깔끔한 시스템을 만들려면 단 몇 줄이라도 중복을 제거하겠다는 의지가 필요하다<br>다음 코드를 살펴보자.</p><figure class="highlight java"><figcaption><span>scaleTOOneDimension 메서드와 rotate 메서드를 살펴보면 일부 코드가 동일</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">scaleToOneDimension</span><span class="params">(<span class="type">float</span> desiredDimension, <span class="type">float</span> imageDimension)</span> {</span><br><span class="line">  <span class="keyword">if</span> (Math.abs(desiredDimension - imageDimension) &lt; errorThreshold)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  <span class="type">float</span> <span class="variable">scalingFactor</span> <span class="operator">=</span> desiredDimension / imageDimension;</span><br><span class="line">  scalingFactor = (<span class="type">float</span>)(Math.floor(scalingFactor * <span class="number">100</span>) * <span class="number">0.01f</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="type">RenderedOp</span> <span class="variable">newImage</span> <span class="operator">=</span> ImageUtilities.getScaledImage(image, scalingFactor, scalingFactor);</span><br><span class="line">  image.dispose();</span><br><span class="line">  System.gc();</span><br><span class="line">  image = newImage;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">rotate</span><span class="params">(<span class="type">int</span> degrees)</span> {</span><br><span class="line">  <span class="type">RenderedOp</span> <span class="variable">newImage</span> <span class="operator">=</span> ImageUtilities.getRotatedImage(image, degrees);</span><br><span class="line">  image.dispose();</span><br><span class="line">  System.gc();</span><br><span class="line">  image = newImage;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>중복제거 : 적은 양이지만 공통 코드를 새 메서드로</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">scaleToOneDimension</span><span class="params">(<span class="type">float</span> desiredDimension, <span class="type">float</span> imageDimension)</span> {</span><br><span class="line">  <span class="keyword">if</span> (Math.abs(desiredDimension - imageDimension) &lt; errorThreshold)</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  <span class="type">float</span> <span class="variable">scalingFactor</span> <span class="operator">=</span> desiredDimension / imageDimension;</span><br><span class="line">  scalingFactor = (<span class="type">float</span>) Math.floor(scalingFactor * <span class="number">10</span>) * <span class="number">0.01f</span>);</span><br><span class="line">  replaceImage(ImageUtilities.getScaledImage(image, scalingFactor, scalingFactor));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">rotate</span><span class="params">(<span class="type">int</span> degrees)</span> {</span><br><span class="line">  replaceImage(ImageUtilities.getRotatedImage(image, degrees));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">replaceImage</span><span class="params">(RenderedOpnewImage)</span> {</span><br><span class="line">  image.dispose();</span><br><span class="line">  System.gc();</span><br><span class="line">  image = newImage;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>리팩터링</p><ul><li>아주 적은 양이지만 공통적인 코드를 새 메서드로</li><li>뽑고 보니 클래스가 SRP를 위반</li><li>그러므로 새로 만든 replaceImage 메서드를 다른 클래스로 옮겨도 좋겠다고 한다</li><li>그러면 새 메서드의 가시성이 높아진다</li><li>따라서 다른 팀원이 새 메서드를 좀 더 추상화해 다른 맥락에서 재사용할 기회를 포착할지도 모른다</li><li>이런 ‘소규모 재사용’은 시스템 복잡도를 극적으로 줄여준다</li><li>소규모 재사용을 제대로 익혀야 대규모 재사용이 가능하다</li></ul><p>템플릿 메서드 패턴 : 고차원 중복을 제거할 목적으로 자주 사용하는 기법</p><figure class="highlight java"><figcaption><span>중간의 법정일수 계산을 제외하면 두 메서드는 거의 동일</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VacationPolicy</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">accrueUSDDivisionVacation</span><span class="params">()</span> {</span><br><span class="line">    <span class="comment">// 지금까지 근무한 시간을 바탕으로 휴가 일수를 계산하는 코드</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 휴가 일수가 미국 최소 법정 일수를 만족하는지 확인하는 코드</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 휴가 일수를 급여 대장에 적용하는 코드</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">accrueEUDivisionVacation</span><span class="params">()</span> {</span><br><span class="line">    <span class="comment">// 지금까지 근무한 시간을 바탕으로 휴가 일수를 계산하는 코드</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 휴가 일수가 유럽연합 최소 법정 일수를 만족하는지 확인하는 코드</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 휴가 일수를 급여 대장에 적용하는 코드</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>템플릿 메서드 패턴 적용</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">abstract</span> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VacationPolicy</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">accrueVacation</span><span class="params">()</span> {</span><br><span class="line">    caculateBseVacationHours();</span><br><span class="line">    alterForLegalMinimums();</span><br><span class="line">    applyToPayroll();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">calculateBaseVacationHours</span><span class="params">()</span> { <span class="comment">/* ... */</span> };</span><br><span class="line">  <span class="keyword">abstract</span> <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">alterForLegalMinimums</span><span class="params">()</span>;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">applyToPayroll</span><span class="params">()</span> { <span class="comment">/* ... */</span> };</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">USVacationPolicy</span> <span class="keyword">extends</span> <span class="title class_">VacationPolicy</span> {</span><br><span class="line">  <span class="meta">@Override</span> <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">alterForLegalMinimums</span><span class="params">()</span> {</span><br><span class="line">    <span class="comment">// 미국 최소 법정 일수를 사용한다.</span></span><br><span class="line">  }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EUVacationPolicy</span> <span class="keyword">extends</span> <span class="title class_">VacationPolicy</span> {</span><br><span class="line">  <span class="meta">@Override</span> <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">alterForLegalMinimums</span><span class="params">()</span> {</span><br><span class="line">    <span class="comment">// 유럽연합 최소 법정 일수를 사용한다.</span></span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>하위 클래스는 중복되지 않는 정보만 제공해 accrueVacation 알고리즘에서 빠진 ‘구멍’을 메운다.</p><h2 id="표현하라"><a href="#표현하라" class="headerlink" title="표현하라"></a>표현하라</h2><h4 id="자신만-이해하는-코드의-문제"><a href="#자신만-이해하는-코드의-문제" class="headerlink" title="자신만 이해하는 코드의 문제"></a>자신만 이해하는 코드의 문제</h4><ul><li>아마 우리 대다수는 엉망인 코드를 접한 경험이 있다</li><li>아마 우리 대다수는 스스로 엉망인 코드를 내놓은 경험도 있다</li><li>자신이 이해하는 코드를 짜기는 쉽다<ul><li>코드를 짜는 동안에는 문제에 푹 빠져 코드를 구석구석 이해하니까</li></ul></li><li>하지만 나중에 코드를 유지보수할 사람이 그만큼 문제를 깊이 이해할 가능성은 희박</li></ul><h4 id="코드는-개발자의-의도를-분명히-표현해야-한다"><a href="#코드는-개발자의-의도를-분명히-표현해야-한다" class="headerlink" title="코드는 개발자의 의도를 분명히 표현해야 한다"></a>코드는 개발자의 의도를 분명히 표현해야 한다</h4><ul><li>소프트웨어 프로젝트 비용 중 대다수는 장기적인 SM에 들어간다</li><li>코드를 변경하면서 버그의 싹을 심지 않으려면 SM 개발자가 시스템을 제대로 이해해야 한다</li><li>하지만 시스템이 점차 복잡<ul><li>SM 개발자가 시스템 이해하느라 보내는 시간은 점점 늘어남</li><li>동시에 코드를 오해할 가능성도 점점 커짐</li></ul></li><li>그러므로 코드는 개발자의 의도를 분명히 표현해야 한다<ul><li>개발자가 코드를 명백하게 짤수록 다른 사람이 그 코드를 이해하기 쉬워진다</li><li>그래야 결함이 줄어들고 유지보수 비용이 적게 든다.</li></ul></li></ul><p>How?</p><ol><li>좋은 이름을 선택</li></ol><ul><li>이름과 기능이 완전히 딴판인 클래스나 함수로 개발자를 놀라게 해서는 안 된다</li></ul><ol start="2"><li>함수와 클래스 크기를 가능한 한 줄인다</li></ol><ul><li>작은 클래스와 작은 함수는 이름 짓기도 쉽고, 구현하기도 쉽고, 이해하기도 쉽다</li></ul><ol start="3"><li>표준 명칭을 사용</li></ol><ul><li>예를 들어, 디자인 패턴은 의사소통과 표현력 강화가 주요 목적</li><li>class가 COMMAND나 VISITOR등의 표준 패턴을 사용하는 경우 클래스 이름에 패턴 이름을 넣어준다</li><li>그러면 다른 개발자가 클래스 설계 의도를 이해하기 쉬워진다.</li></ul><ol start="4"><li>단위 테스트 케이스를 꼼꼼히 작성</li></ol><ul><li>테스트 케이스는 소위 ‘예제로 보여주는 문서’다</li><li>다시 말해, 잘 만든 테스트 케이스를 읽어보면 클래스 기능이 한눈에 들어온다</li></ul><ol start="5"><li>노력과 주의 : 표현력을 높이는 가장 중요한 방법</li></ol><ul><li>흔히 코드만 돌린 후 다음 문제로 직행하는 사례가 너무도 흔하다</li><li>나중에 읽을 사람을 고려해 조금이라도 읽기 쉽게 만드려는 충분한 고민은 거의 찾기 어렵다</li><li>하지만 나중에 코드를 읽을 사람은 바로 자신일 가능성이 높다는 사실을 명심하자</li><li>그러므로 자신의 작품을 조금 더 자랑하자<ul><li>함수와 클래스에 조금 더 시간을 투자하자</li><li>더 나은 이름을 선택</li><li>큰 함수를 작은 함수 여럿으로 나누고</li><li>자신의 작품에 조금만 더 주의를 기울이자</li><li>주의는 대단한 재능이다.</li></ul></li></ul><h2 id="클래스와-메서드-수를-최소로-줄여라"><a href="#클래스와-메서드-수를-최소로-줄여라" class="headerlink" title="클래스와 메서드 수를  최소로 줄여라"></a>클래스와 메서드 수를  최소로 줄여라</h2><h4 id="가능한-최소의-의미"><a href="#가능한-최소의-의미" class="headerlink" title="(가능한)최소의 의미"></a>(가능한)최소의 의미</h4><ul><li>중복을 제거하고, 의도를 표현하고, SRP를 준수한다는 기본적인 개념도 극단으로 치달으면 득보다 실이 많아진다</li><li>클래스와 메서드 크기를 줄이자고 조그만 클래스와 메서드를 수없이 만드는 사례도 없지 않다</li><li>그래서 이 규칙은 함수와 클래스 수를 가능한 한 줄이라고 제안한다</li></ul><h4 id="독단을-멀리하고-실용성-선택"><a href="#독단을-멀리하고-실용성-선택" class="headerlink" title="독단을 멀리하고 실용성 선택"></a>독단을 멀리하고 실용성 선택</h4><ul><li>때로는 무의미하고 독단적인 정책 탓에 클래스 수와 메서드 수가 늘어나기도 한다</li><li>클래스마다 무조건 인터페이스를 생성하라고 요구하는 구현 표준이 좋은 예</li><li>자료 클래스와 동작 클래스는 무조건 분리해야 한다고 주장하는 개발자도 좋은 예</li><li>가능한 독단적인 견해는 멀리하고 실용적인 방식을 택해야 한다</li></ul><h4 id="해당-규칙의-목표와-우선순위에-대하여"><a href="#해당-규칙의-목표와-우선순위에-대하여" class="headerlink" title="해당 규칙의 목표와 우선순위에 대하여"></a>해당 규칙의 목표와 우선순위에 대하여</h4><ul><li>목표는 함수와 클래스 크기를 작게 유지하면서 동시에 시스템 크기도 작게 유지하는 데 있다</li><li>하지만 이 규칙은 간단한 설계 규칙 네 개 중 우선순위가 가장 낮다</li><li>다시 말해, 클래스와 함수 수를 줄이는 작업도 중요하지만, 테스트 케이스를 만들고 중복을 제거하고 의도를 표현하는 작업이 더 중요하다는 뜻</li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li>경험을 대신할 단순한 개발 기법은 없음</li><li>이 책에서 소개하는 기법은 저자들이 수십 년 동안 쌓은 경험의 정수</li><li>단순한 설계 규칙을 따른다면 (오랜 경험 후에야 익힐) 우수한 기법과 원칙을 단번에 활용할 수 있다</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch12/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅺ. 시스템</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch11/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch11/</guid>
      <pubDate>Wed, 09 Feb 2022 00:55:08 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Related-POST&quot;&gt;&lt;a href=&quot;#Related-POST&quot; class=&quot;headerlink&quot; title=&quot;Related POST&quot;&gt;&lt;/a&gt;Related POST&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅺ-시스템"><a href="#Ⅺ-시스템" class="headerlink" title="Ⅺ. 시스템"></a>Ⅺ. 시스템</h1><h2 id="도시를-세운다면"><a href="#도시를-세운다면" class="headerlink" title="도시를 세운다면?"></a>도시를 세운다면?</h2><p>도시를 새로 세운다고 했을 때 혼자서 세부 사항까지 혼자서 관리 가능한가?</p><ul><li>불가능, 만들어진 도시의 유지보수도 한 사람은 무리</li><li>그래도 도시가 돌아가는 이유?<ol><li>각 분야를 그리는 팀이 존재 : 수도 관리, 전력 관리, 교통 관리, 치안 관리, 건축물 관리 등<br>→ 큰 그림을 그리는 사람들도, 작은 사항에 집중하는 사람들도 있음</li><li>적절한 추상화와 모듈화<br>  → 큰 그림을 이해하지 못해도 개인과 개인이 관리하는 ‘구성 요소’는 효율적으로 돌아간다<br>SW 팀도 도시와 같다</li></ol></li><li>왜 개발팀의 시스템은 비슷한 수준의 관심사 분리나 추상화를 하지 못할까?</li><li>클린코드! → 클린코드를 구현하면 낮은 추상화 수준에서 관심사 분리가 쉬워진다</li></ul><h2 id="시스템의-생성과-사용을-분리하라"><a href="#시스템의-생성과-사용을-분리하라" class="headerlink" title="시스템의 생성과 사용을 분리하라"></a>시스템의 생성과 사용을 분리하라</h2><ul><li>제작(construction)과 사용(use)은 아주 다르다<ul><li>호텔 예<ul><li>건축 중엔 기중기와 승강기와 철골들이 붙어 있고 작업복을 착용한 사람들만 존재</li><li>건축 후엔 위의 것들이 전부 사라진 예쁜 색상의 벽과 세련된 복장의 호텔리어들만 존재</li></ul></li><li>개발도 마찬가지 : <strong>준비 과정과 런타임 로직을 분리해야한다</strong> (관심사 분리)<ul><li>준비 과정 : 어플리케이션 객체를 제작하고 의존성을 서로 바인드 하는 과정</li><li>런타임 과정 : 준비 과정 이후 실제 실행하는 과정</li></ul></li></ul></li><li>관심사 분리 : 우리 분야에서 가장 오래되고 가장 중요한 설계 기법 중 하나</li></ul><p>시작 단계</p><ul><li>모든 어플리케이션이 풀어야 하는 관심사(concern)</li><li>불행히도 대다수 어플리케이션은 이 시작단계 관심사를 분리하지 않음</li><li>준비과정 코드를 주먹구구식으로 구현한채 런타임 로직과 섞여있음</li></ul><figure class="highlight java"><figcaption><span>전형적인 주먹구구식 시작단계의 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> Service <span class="title function_">getService</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">if</span> (service == <span class="literal">null</span>)</span><br><span class="line">        service = <span class="keyword">new</span> <span class="title class_">MyServiceImpl</span>(...); <span class="comment">// Good enough default for most cases?</span></span><br><span class="line">    <span class="keyword">return</span> service;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Lazy Init</p><ul><li>초기화 지연</li><li>장점 : 여러가지<ul><li>실제로 필요할 때까지 객체를 생성X → 불필요한 객체 초기화 비용 절약 → 시작 시간 빨라짐</li><li>어떤 경우에도 null 포인터를 반환하지 않는다</li></ul></li><li>단점<ul><li><code>getService</code> 메서드의 명시적 의존성 : MyServiceImpl, (생략된) 생성자 파라미터</li><li>런타임 로직에서 MyServiceImpl 객체를 전혀 사용하지 않더라도 의존성을 해결하지 않으면 컴파일이 안됨</li><li>테스트의 어려움<ul><li>MyServiceImpl이 무거운 객체라면 단위테스트시 테스트 더블이나 mocking 객체로 할당해야 함</li><li>객체 생성 로직이 런타임에 섞여있으므로 null일떄와 null이 아닐때등 모든 실행경로 테스트해야 함</li><li>책임이 둘 → 메서드가 작업을 2가지 이상 → 작게나마 SRP 위반</li></ul></li><li>MyServiceImpl이 모든 상황에 적합한 객체인지 모름<ul><li>가장 큰 문제</li><li>getService 메서드를 포함하는 클래스가 MyServiceImpl에 대한 전체 문맥을 알 필요가 있나?</li><li>현실적으로 한 객체 유형이 모든 문맥에 적합할 가능성이 있는가?</li></ul></li></ul></li><li>많은 어플리케이션이 이처럼 좀스러운 설정 기법을 수시로 사용하는게 문제<ul><li>한 번 정도였으면 그리 심각한 문제가 아님</li><li>수시 사용 → 심각</li><li>전반적 설정 방식이 어플리케이션 곳곳에 흩어짐</li><li>모듈성 저조, 중복 심각</li><li>생성/사용의 분산</li></ul></li></ul><p>체계적/탄탄한 시스템을 만들고 싶다면?</p><ul><li>좀스럽고 손쉬운 기법으로 모듈성을 깨서는 절대로 안 된다</li><li>설정 논리는 일반 실행 논리와 분리해야 모듈성이 높아진다</li><li>주요 의존성을 해소하기 위한 전반적이며 일관적인 방식이 필요</li></ul><h3 id="Main-분리"><a href="#Main-분리" class="headerlink" title="Main 분리"></a>Main 분리</h3><ul><li>시스템 생성, 시스템 사용을 분리한 한 가지 방법<ul><li>생성과 관련한 코드는 모두 main이나 main이 호출하는 모듈로 옮긴다</li><li>나머지 시스템은 모든 객체가 생성되고 모든 의존성이 연결되었다고 가정<img src="/dev-book/cleancode/CleanCode-ch11/figure-11-1.png" class="" title="`main()`으로부터 생성 분리"></li></ul></li><li>제어흐름 따라가기가 쉬움<ul><li><code>main()</code>에서 시스템 필요한 객체 생성한 후 이를 어플리케이션에 넘긴다</li><li>어플리케이션은 그저 객체를 사용할 뿐</li></ul></li><li>화살표 방향 주목<ul><li>모든 화살표가 main 에서 어플리케이션 방향</li><li>즉, 어플리케이션은 main이나 객체 생성 과정을 전혀 모른다</li><li>단지 모든 객체가 적절히 생성되었다고 가정하고 디자인 집중</li></ul></li></ul><h3 id="팩토리-기법"><a href="#팩토리-기법" class="headerlink" title="팩토리 기법"></a>팩토리 기법</h3><ul><li><p>때로는 객체가 생성되는 시점을 어플리케이션에서 결정할 필요가 있음</p></li><li><p>예시) 주문 처리 시스템에서 어플리케이션은 LineItem 인스턴스를 생성해 Order에 추가</p></li><li><p><code>ABSTRACT FACTORY PATTERN</code> 사용</p><ul><li>Factory Method Pattern<ul><li>조건에 따른 객체 생성을 팩토리 클래스로 위임하여, 팩토리 클래스에서 객체를 생성하는 패턴</li></ul></li><li>추상 팩토리 패턴<ul><li>서로 관련이 있는 객체들을 통째로 묶어서 팩토리 클래스로 만들고, 이들 팩토리를 조건에 따라 생성하도록 다시 팩토리를 만들어서 객체를 생성하는 패턴</li></ul></li></ul></li><li><p>LineItem 생성 시점을 어플리케이션이 결정하나 LineItem 생성 코드는 어플리케이션이 알지 못함</p><img src="/dev-book/cleancode/CleanCode-ch11/figure-11-2.png" class="" title="팩토리로 생성 분리"></li><li><p>여기서도 모든 의존성이  main 에서 OrderProcessing 어플리케이션 방향</p><ul><li>즉, OrderProcessing 어플리케이션은 LineItem이 생성되는 구체적 방법 모름</li><li>구체적 방법 : main에 있는 LineItemFactoryImplementation</li></ul></li><li><p>그럼에도</p><ul><li>OrderProcessing 어플리케이션은 LineItem 인스턴스가 생성되는 시점을 완벽하게 통제한다</li><li>필요시 OrderProcessing 어플리케이션에서만 사용하는 생성자 인수도 넘길 수 있음</li></ul></li></ul><h3 id="의존성-주입-Dependency-Injection"><a href="#의존성-주입-Dependency-Injection" class="headerlink" title="의존성 주입(Dependency Injection)"></a>의존성 주입(Dependency Injection)</h3><ul><li>사용과 제작을 분리하는 강력한 매커니즘의 하나</li><li>DI: 제어 역전(IoC) 기법을 의존성 관리에 적용한 매커니즘</li><li>IoC<ul><li>한 객체가 맡은 보조 책임을 새로운 객체에게 전적으로 떠넘김</li><li>새로운 객체는 남겨받은 책임만 맡음 → SRP 지킴</li><li>객체는 의존성 자체를 인스턴스로 만드는 책임은 지지 않는다 : 의존성 관리 맥락</li><li>그럼 의존성 차체를 인스턴스로 만드는 책임은? → 다른 전담 매커니즘에 넘긴다 → 제어 역전</li><li>초기 설정은 시스템 전체에서 필요 → 책임질 매커니즘으로 main이나 특수 컨테이너 사용</li></ul></li><li>JNDI 검색 : DI ‘부분적’ 구현 예시<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">MyService</span> <span class="variable">myService</span> <span class="operator">=</span> (MyService)(jndiContext.lookup(“NameOfMyService”));</span><br></pre></td></tr></tbody></table></figure><ul><li>객체는 디렉터리 서버에 이름을 제공하고 그 이름과 일치하는 서비스를 요청</li><li>호츨하는 객체는 (반환 객체가 적절한 인터페이스를 구현하는 한) 반환 객체 유형 제어 안함</li><li>호출하는 객체는 의존성을 능동적으로 해결 - 절반의 DI</li></ul></li><li>진정한 DI : 한걸음 더 나아감 → <strong>클래스가 의존성을 해결하려 시도하지 않는다</strong><ul><li>클래스 : 완전 수동적</li><li>의존성을 주입하는 방법 : setter, 생성자 인수 (혹은 둘 다 가능)</li><li>DI Container<ul><li>(대개 요청이 들어올 떄마다)필요한 객체의 인스턴스 생성후 위에서 말한 주입 방법의 메서드로 의존성 설정</li><li>실제 생성되는 객체 유형 : 설정파일에서 지정하거나 특수 생성 모듈에서 코드로 명시</li><li>스프링: 가장 널리 알려진 DI 컨테이너 제공</li></ul></li></ul></li><li>DI와 초기화 지연<ul><li>DI를 사용해도 때로 초기화 지연이 유용하다</li><li>많은 DI 컨테이너는 필요할 때까지 객체 생성 X, 계산지연이나 비슷한 최적화 활용한 방법 제공        </li><li>팩토리 호출이라던지, 프록시를 생성하는 방법을 제공한다던지</li></ul></li></ul><h2 id="확장-scaling"><a href="#확장-scaling" class="headerlink" title="확장 (scaling)"></a>확장 (scaling)</h2><ul><li><p>촌락은 마을로, 마을은 도시로 성장한다</p></li><li><p>그러면서 전력, 상수도, 인터넷 등의 서비스도 생겨난다</p></li><li><p>성장의 고통</p><ul><li>꽉 막힌 도로에서 왜 처음부터 넓게 만들지 않았는가 불평</li><li>하지만 누가 마을의 성장을 고려해 미리 6차선 고속도로를 지으려 할까?</li></ul></li><li><p>처음부터 시스템을 제대로 제대로 만든다는 것은 미신일 뿐이다.</p><ul><li>우리는 오늘 필요한 것을 주어진 요청사항과  사용자 스토리에 맞춰 구현할 뿐이다</li><li>내일은 내일 주어질 새로운 스토리에 맞춰 시스템을 조정/확장하면 된다</li><li>이것이 Agile의 핵심 : 반복적, 점진적</li></ul></li></ul><p>시스템 수준의 아키텍처</p><ul><li>코드레벨의 시스템 조정/확장 쉽게? TDD, Refactoring, 그리고 이 둘의 산물인 clean Code가 역할</li><li>시스템 수준에서는?</li><li>단순한 아키텍처를 복잡한 아키텍처로 조금씩 키울 수 없다는 현실?<ul><li>sw 시스템은 물리적 시스템과 다르다</li><li>Concern들을 적절히 분리할 수 있다면, sw 시스템은 물리적인 시스템과는 다르게 점진적으로 커질 수 있다.</li></ul></li></ul><p>Concern들을 적절히 분리하지 못하는 아키텍처 예 : EJB1, EJB2 </p><ol><li>클라이언트가 사용할 (프로세스 내)의 지역 인터페이스나 (다른 JVM)의 원격 인터페이스 정의 필요</li></ol><figure class="highlight java"><figcaption><span>BANK EJB 지역 인터페이스</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.banking;</span><br><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> javax.ejb.*;</span><br><span class="line"><span class="comment">// 열거 속성: 은행 주소, 은행이 소유하는 계좌</span></span><br><span class="line"><span class="comment">// 각 계좌 정보는 Account EJB로 처리</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">BankLocal</span> <span class="keyword">extends</span> <span class="title class_">java</span>.ejb.EJBLocalObject {</span><br><span class="line">    String <span class="title function_">getStreetAddr1</span><span class="params">()</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    String <span class="title function_">getStreetAddr2</span><span class="params">()</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    String <span class="title function_">getCity</span><span class="params">()</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    String <span class="title function_">getState</span><span class="params">()</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    String <span class="title function_">getZipCode</span><span class="params">()</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setStreetAddr1</span><span class="params">(String street1)</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setStreetAddr2</span><span class="params">(String street2)</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setCity</span><span class="params">(String city)</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setState</span><span class="params">(String state)</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setZipCode</span><span class="params">(String zip)</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    Collection <span class="title function_">getAccounts</span><span class="params">()</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setAccounts</span><span class="params">(Collection accounts)</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">addAccount</span><span class="params">(AccountDTO accountDTO)</span> <span class="keyword">throws</span> EJBException;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>상응하는 EJB2 엔티티빈 구현</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 위 소스의 인터페이스를 구현한 BANK bean 구현 클래스</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">package</span> com.example.banking;</span><br><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> javax.ejb.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Bank</span> <span class="keyword">implements</span> <span class="title class_">javax</span>.ejb.EntityBean {</span><br><span class="line">    <span class="comment">// 비지니스 로직</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> String <span class="title function_">getStreetAddr1</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> String <span class="title function_">getStreetAddr2</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> String <span class="title function_">getCity</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> String <span class="title function_">getState</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> String <span class="title function_">getZipCode</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setStreetAddr1</span><span class="params">(String street1)</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setStreetAddr2</span><span class="params">(String street2)</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setCity</span><span class="params">(String city)</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setState</span><span class="params">(String state)</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setZipCode</span><span class="params">(String zip)</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> Collection <span class="title function_">getAccounts</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setAccounts</span><span class="params">(Collection accounts)</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addAccount</span><span class="params">(AccountDTO accountDTO)</span> {</span><br><span class="line">        <span class="type">InitialContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InitialContext</span>();</span><br><span class="line">        <span class="type">AccountHomeLocal</span> <span class="variable">accountHome</span> <span class="operator">=</span> context.lookup(<span class="string">"AccountHomeLocal"</span>);</span><br><span class="line">        <span class="type">AccountLocal</span> <span class="variable">account</span> <span class="operator">=</span> accountHome.create(accountDTO);</span><br><span class="line">        <span class="type">Collection</span> <span class="variable">accounts</span> <span class="operator">=</span> getAccounts();</span><br><span class="line">        accounts.add(account);</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// EJB 컨테이너 로직</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(Integer id)</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> Integer <span class="title function_">getId</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">ejbCreate</span><span class="params">(Integer id)</span> { ... }</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">ejbPostCreate</span><span class="params">(Integer id)</span> { ... }</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 나머지도 구현해야 하지만 일반적으로 이렇게 비어있다.</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setEntityContext</span><span class="params">(EntityContext ctx)</span> {}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unsetEntityContext</span><span class="params">()</span> {}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">ejbActivate</span><span class="params">()</span> {}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">ejbPassivate</span><span class="params">()</span> {}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">ejbLoad</span><span class="params">()</span> {}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">ejbStore</span><span class="params">()</span> {}</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">ejbRemove</span><span class="params">()</span> {}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>특징</p><ul><li>객체 생성 팩토리인 LocalHome 인터페이스 : 생략</li><li>기타 BANK의 탐색(query) 메서드 : 생략</li><li>마지막으로 XML deployment descriptors 작성 필요<ul><li>영구 저장소에서 객체와 관계형 자료가 매핑되는 방식</li><li>원하는 트랜잭션 동작 방식</li><li>보안 제약 조건등</li></ul></li><li><strong>비지니스 논리가 EJB2 어플리케이션 ‘컨테이너’에 강하게 결합됨</strong><ul><li>클래스 생성시 컨테이너에서 subclass해야 한다</li><li>컨테이너가 요구하는 다양한 lifecycle 메서드 구현해야 함</li></ul></li><li>so?<ul><li>독자적 단위 테스트 어려움<ul><li>많은 시간을 낭비하며 EJB와 테스트를 실제 서버에 배치해야함</li><li>컨테이너 흉내도 쉽지 않음</li><li>EJB2 코드는 프레임워크 밖에서 재사용이 사실상 불가능</li></ul></li></ul></li><li>OOP라는 개념조차 뿌리가 흔들림</li><li>상속 불가능</li><li>쓸데 없는 DTO 작성 요구</li></ul><h3 id="Cross-Cutting-Concerns-횡단-관심-분야"><a href="#Cross-Cutting-Concerns-횡단-관심-분야" class="headerlink" title="Cross-Cutting Concerns(횡단 관심 분야)"></a>Cross-Cutting Concerns(횡단 관심 분야)</h3><p>보안, 영속성등의 concern: 어플리케이션의 자연스러운 객체 경계를 넘나드는 특성</p><ul><li>EJB2 아키텍처는 소스 코드가 아니라 배치 기술자에 해당 부분 정의 → 사실상 AOP 예견</li><li>모든 객체가 전반적으로 동일한 방식을 이용하게 만들어야 한다</li><li>원론적으로는 모듈화/캡슐화된 방식으로 영속성 방식 구상</li><li>현실적으로는 온갖 객체로 흩어지게된다.</li></ul><p>횡단 관심사</p><ul><li>영속성 프레임워크, 도메인 논리 모듈화 가능하지만 두 영역이 세밀하게 겹친다</li></ul><p>AOP</p><ul><li>aspect-oriented programming</li><li>트랜잭션, 로깅등의 횡단 관심사의 모듈성을 되살림</li><li>“코드의 어떤 부분에 어떤 추가적 기능을 삽입할까?” → 해당 정의를 aspect로 제공</li><li>Aspect라는 모듈 구성 개념<ul><li>“특정 관심사 지원을 위해선 시스템에서 특정 지점들이 동작하는 방식을 일관성 있게 바꿔야 한다”라고 명시</li></ul></li></ul><h2 id="자바에서-사용하는-관점-혹은-유사한-매커니즘-3가지"><a href="#자바에서-사용하는-관점-혹은-유사한-매커니즘-3가지" class="headerlink" title="자바에서 사용하는 관점 혹은 유사한 매커니즘 3가지"></a>자바에서 사용하는 관점 혹은 유사한 매커니즘 3가지</h2><h3 id="자바-프록시"><a href="#자바-프록시" class="headerlink" title="자바 프록시"></a>자바 프록시</h3><ul><li>단순 상황 적합, 좋은 예시 : 개별 객체, 클래스에서 메서드 호출 wraaping하는 경우</li><li>JDK의 Dynamic Proxy는 인터페이스만 지원<ul><li>class 프록시 사용하려면 bytecode 처리 라이브러리 필요 : CGLIB, ASM, Javassist등</li></ul></li></ul><figure class="highlight java"><figcaption><span>JDK 프록시를 사용해서 영속성을 지원하는 예제</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Bank.java (패키지 이름을 감춘다)</span></span><br><span class="line"><span class="keyword">import</span> java.utils.*;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 은행 추상화</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Bank</span> {</span><br><span class="line">    Collection&lt;Account&gt; <span class="title function_">getAccounts</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">setAccounts</span><span class="params">(Collection&lt;Account&gt; accounts)</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// BankImpl.java</span></span><br><span class="line"><span class="keyword">import</span> java.utils.*;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 추상화를 위한 POJO 구현</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BankImpl</span> <span class="keyword">implements</span> <span class="title class_">Bank</span> {</span><br><span class="line">    <span class="keyword">private</span> List&lt;Account&gt; accounts;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Collection&lt;Account&gt; <span class="title function_">getAccounts</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> accounts;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAccounts</span><span class="params">(Collection&lt;Account&gt; accounts)</span> {</span><br><span class="line">        <span class="built_in">this</span>.accounts = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Account&gt;();</span><br><span class="line">        <span class="keyword">for</span> (Account account: accounts) {</span><br><span class="line">            <span class="built_in">this</span>.accounts.add(account);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// BankProxyHandler.java</span></span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.*;</span><br><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="comment">// “InvocationHandler” required by the proxy API.</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BankProxyHandler</span> <span class="keyword">implements</span> <span class="title class_">InvocationHandler</span> {</span><br><span class="line">    <span class="keyword">private</span> Bank bank;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BankProxyHandler</span><span class="params">(Bank bank)</span> {</span><br><span class="line">        <span class="built_in">this</span>.bank = bank;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// InvocationHandler 내부 정의 메서드 </span></span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable {</span><br><span class="line">        <span class="type">String</span> <span class="variable">methodName</span> <span class="operator">=</span> method.getName();</span><br><span class="line">        <span class="keyword">if</span> (methodName.equals(<span class="string">"getAccounts"</span>)) {</span><br><span class="line">            bank.setAccounts(getAccountsFromDatabase());</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> bank.getAccounts();</span><br><span class="line">        } <span class="keyword">else</span> <span class="keyword">if</span> (methodName.equals(<span class="string">"setAccounts"</span>)) {</span><br><span class="line">            bank.setAccounts((Collection&lt;Account&gt;) args[<span class="number">0</span>]);</span><br><span class="line">            setAccountsToDatabase(bank.getAccounts());</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            ...</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 세부사항..</span></span><br><span class="line">    <span class="keyword">protected</span> Collection&lt;Account&gt; <span class="title function_">getAccountsFromDatabase</span><span class="params">()</span> { ... }</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setAccountsToDatabase</span><span class="params">(Collection&lt;Account&gt; accounts)</span> { ... }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 다른 곳에 위치하는 코드</span></span><br><span class="line"><span class="type">Bank</span> <span class="variable">bank</span> <span class="operator">=</span> (Bank) Proxy.newProxyInstance(</span><br><span class="line">    Bank.class.getClassLoader(),</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Class</span>[] { Bank.class },</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">BankProxyHandler</span>(<span class="keyword">new</span> <span class="title class_">BankImpl</span>())</span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure><p>설명</p><ul><li><p>리플렉션 API 사용</p><ul><li>직접 다이나믹 프록시 구현시 인터페이스 구현을 해야하고 프록시 클래스 내부에 중복 발생</li><li>첫 번째 인자: 프록시를 만들 클래스 로더</li><li>두 번째 인자: 어떤 인터페이스에 대해 프록시를 만들 것인지 명시</li><li>세 번째 인자: InvocationHandler 인터페이스의 구현체</li><li>리턴 값: 동적으로 만든 프록시 객체</li></ul></li><li><p>Java Proxy API를 위한 Bank 인터페이스를 작성한다.</p></li><li><p>위에서 작성한 Bank 인터페이스를 사용한 BankImpl(POJO aka Plane Old Java Object)를 구현한다.</p><ul><li>여기에는 순수한 데이터만 들어가며 비지니스 로직은 포함되지 않는다.(모델과 로직의 분리)</li></ul></li><li><p>InvocationHandler를 구현하는 BankProxyHandler를 작성한다.</p><ul><li>이 핸들러는 Java Reflection API를 이용해 Bank 인터페이스를 구현하는<br>객체들의 메서드콜을 가로챌 수 있으며 추가적인 로직을 삽입할 수 있다.</li><li>본 예제에서 비지니스 로직(persistant stack logic)은 이 곳에 들어간다.</li></ul></li><li><p>마지막으로 코드의 마지막 블럭과 같이 BankImpl 객체를 BankProxyHandler에 할당한다</p><ul><li>Bank 인터페이스를 사용해 프록시된 인터페이스를 사용해 모델과 로직이 분리된 코드를 작성가능</li><li>이로써 모델과 로직의 분리를 이뤄낸 코드를 작성할 수 있게 되었다.</li></ul></li></ul><p>단점 : 코드의 양과 크기 → 클린 코드 작성하기 어렵다</p><ul><li>단순 예제인데도 코드가 상당히 많으며 복잡하다</li><li>바이트 조작 라이브러리 사용해도 어렵</li><li>프록시는 (진정한 AOP해법에 필요한) 시스템 단위로 실행’지점’을 명시하는 매커니즘도 제공 X</li></ul><h3 id="순수-자바-AOP-프레임워크"><a href="#순수-자바-AOP-프레임워크" class="headerlink" title="순수 자바 AOP 프레임워크"></a>순수 자바 AOP 프레임워크</h3><ul><li>대두분의 프록시 코드 → 틀에 박힌 판박이 → 도구로 자동화 가능</li><li>Spring AOP, JBoss AOP등 → 내부적으로는 프록시 사용</li><li>스프링 : 비지니스 논리를 POJO로 구현<ul><li>POJO<ul><li>순수하게 도메인 초점</li><li>프레임워크나 다른 도메인에도 의존하지 않음 → 테스트가 더 쉽고 간단</li><li>상대적 단순 → 사용자 스토리를 올바로 구현 쉬움 → 유지보수도 좋음</li></ul></li></ul></li><li>어플리케이션 기반 구조 : 프로그래머가 설정파일이나 API 사용<ul><li>영속성, 트랜잭션, 보안, 캐시, 장애조치등의 횡단 관심사</li><li>사용자가 모르게 프록시나 bytecode 라이브러리로 구현</li></ul></li></ul><figure class="highlight java"><figcaption><span>전형적인 스프링 2.x 설정 파일</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&lt;beans&gt;</span><br><span class="line">    ...</span><br><span class="line">    &lt;bean id=<span class="string">"appDataSource"</span></span><br><span class="line">        class=<span class="string">"org.apache.commons.dbcp.BasicDataSource"</span></span><br><span class="line">        destroy-method=<span class="string">"close"</span></span><br><span class="line">        p:driverClassName=<span class="string">"com.mysql.jdbc.Driver"</span></span><br><span class="line">        p:url=<span class="string">"jdbc:mysql://localhost:3306/mydb"</span></span><br><span class="line">        p:username=<span class="string">"me"</span>/&gt;</span><br><span class="line">    </span><br><span class="line">    &lt;bean id=<span class="string">"bankDataAccessObject"</span></span><br><span class="line">        class=<span class="string">"com.example.banking.persistence.BankDataAccessObject"</span></span><br><span class="line">        p:dataSource-ref=<span class="string">"appDataSource"</span>/&gt;</span><br><span class="line">    </span><br><span class="line">    &lt;bean id=<span class="string">"bank"</span></span><br><span class="line">        class=<span class="string">"com.example.banking.model.Bank"</span></span><br><span class="line">        p:dataAccessObject-ref=<span class="string">"bankDataAccessObject"</span>/&gt;</span><br><span class="line">    ...</span><br><span class="line">&lt;/beans&gt;</span><br></pre></td></tr></tbody></table></figure><img src="/dev-book/cleancode/CleanCode-ch11/figure-11-3.png" class="" title="데코레이터의 `러시안 인형`"><ul><li>Bank 도메인 객체는 BankDataAccessObject(DAO)로 프록시</li><li>DAO(자료 접근자 객체)는 JDBC 드라이버 자료 소스로 프록시</li><li>클라이언트는 Bank 객체의 getAccount()에 접근하고 있다고 생각하지만<br>Bank POJO 기본 동작을 확장한 중첩 decorator 객체 집합의 가장 외각과 통신</li><li>필요시 트랜잭션, 캐싱등에 데코레이터 추가 가능<figure class="highlight java"><figcaption><span>실제 BANK 객체 요청 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">XmlBeanFactory</span> <span class="variable">bf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XmlBeanFactory</span>(<span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">"app.xml"</span>, getClass()));</span><br><span class="line"><span class="type">Bank</span> <span class="variable">bank</span> <span class="operator">=</span> (Bank) bf.getBean(<span class="string">"bank"</span>);</span><br></pre></td></tr></tbody></table></figure></li><li>스프링 관련 자바 코드 거의 없음 → <strong>사실상 스프링과 독립적</strong> → EJB2의 tight-coupling 해결</li><li>XML : 장황, 읽기 어려움 → 그럼에도 정책이 겉으로 보이지 않게 자동 생서된 프록시, 관점 논리보다는 단순</li><li>이런 매력적인 아키텍처 → EJB3를 완전히 뜯어 고치게 됨</li><li>EJB3 : 스프링 모델 따라한다 (XML 설정파일, 자바 어노테이션 사용)<figure class="highlight java"><figcaption><span>EJB3 Bank EJB</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Code 3-4(Listing 11-5): An EBJ3 Bank EJB */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">package</span> com.example.banking.model;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.persistence.*;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.Collection;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Entity</span></span><br><span class="line"><span class="meta">@Table(name = "BANKS")</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Bank</span> <span class="keyword">implements</span> <span class="title class_">java</span>.io.Serializable {</span><br><span class="line">    <span class="meta">@Id</span> <span class="meta">@GeneratedValue(strategy=GenerationType.AUTO)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> id;</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Embeddable</span> <span class="comment">// An object “inlined” in Bank’s DB row</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Address</span> {</span><br><span class="line">        <span class="keyword">protected</span> String streetAddr1;</span><br><span class="line">        <span class="keyword">protected</span> String streetAddr2;</span><br><span class="line">        <span class="keyword">protected</span> String city;</span><br><span class="line">        <span class="keyword">protected</span> String state;</span><br><span class="line">        <span class="keyword">protected</span> String zipCode;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Embedded</span></span><br><span class="line">    <span class="keyword">private</span> Address address;</span><br><span class="line">    <span class="meta">@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="bank")</span></span><br><span class="line">    <span class="keyword">private</span> Collection&lt;Account&gt; accounts = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Account&gt;();</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getId</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setId</span><span class="params">(<span class="type">int</span> id)</span> {</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addAccount</span><span class="params">(Account account)</span> {</span><br><span class="line">        account.setBank(<span class="built_in">this</span>);</span><br><span class="line">        accounts.add(account);</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Collection&lt;Account&gt; <span class="title function_">getAccounts</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> accounts;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAccounts</span><span class="params">(Collection&lt;Account&gt; accounts)</span> {</span><br><span class="line">        <span class="built_in">this</span>.accounts = accounts;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>설명</li><li>깔끔한 코드 → 테스트하고 유지 보수가 쉬어짐</li><li>일부 어노테이션의 영속성 정보 존재<ul><li>필요하면 XML 기술자로 옮겨도 된다</li><li>그러면 진짜 순수 POJO만 남는다</li></ul></li></ul><h3 id="AspectJ-관점"><a href="#AspectJ-관점" class="headerlink" title="AspectJ 관점"></a>AspectJ 관점</h3><ul><li>분심사를 관점으로 분리하는 가장 강력한 도구 : AspectJ</li><li>언어 차원에서 관점 모듈화 구성 지원하는 자바 언어 확장</li><li>스프링AOP, JBoss AOP의 순수 자바 방식은 관점 필요한 상황의 80-90% 충분</li><li>장점 : 강력한 기능</li><li>단점 : 새 도구 사용, 새 언어 문법, 새로운 사용법</li><li>‘애너테이션 폼’ : 자바5 애너테이션 사용 관점 정의</li><li>스프링을 사용 : AspectJ에 미숙한 팀이 애너테이션 기반 관점 쉽게 사용하도록 지원</li></ul><h2 id="테스트-주도-시스템-아키텍처-구축-Test-Drive-the-System-Architecture"><a href="#테스트-주도-시스템-아키텍처-구축-Test-Drive-the-System-Architecture" class="headerlink" title="테스트 주도 시스템 아키텍처 구축(Test Drive the System Architecture)"></a>테스트 주도 시스템 아키텍처 구축(Test Drive the System Architecture)</h2><p>횡단 관심사 분리 방식의 위력:<br>→ 어플리케이션 도메인의 논리를 POJO로 작성 가능<br>→ 코드 수준에서 아키텍처 관심사 분리 가능<br>→ 진정한 테스트 주도 아키텍처 구축 가능<br>→ 필요시 새로운 기술을 채택히 단순 아키텍처를 복잡한 아키텍처로 키우는게 가능<br>→ BDUF(구현 시작전에 앞으로 벌어질 모든 사항을 설계하는 기법)을 추구할 필요가 없음</p><ul><li>디테일 말고 전체 시스템은 한번에 설계가 되어야 한다고 추구하는 기법의 반대</li><li>BDUF는 심지어 해롭다<ul><li>처음에 쏟아 부은 노력을 버리지 않으려는 심리적 저항</li><li>처음 아키텍처가 향후 사고방식에 미치는 영향 → 변경을 쉽사리 수용 못함</li></ul></li></ul><p>건축과 다르다</p><ul><li>건축가는 BDUF : 일단 지으면 극적인 변화가 불가능</li><li>SW도 나름 형체가 존재는 한다(Software physics)</li><li>하지만 sw구조가 관점을 효과적으로 분리한다면, 극적인 변화가 경제적으로 가능</li><li>‘아주 단순한’ 아키텍처로 진행한 후 조금씩 확장이 가능하다는 이야기</li><li>그렇다고 ‘아무 방향없이’ 프로젝트 뛰어들면 되는게 아님<ul><li>프로젝트 시작시 일반적인 범위, 목표, 일정, 결과 시스템의 일반적 구조도 고려해야 함</li><li>하지만 변하는 환경 대처하여 진로를 변경할 능력도 반드시 유지 필요</li></ul></li></ul><p>과유 불급</p><ul><li>초창기 EJB : 기술을 너무 많이 넣음 → 관심사 제대로 분리 X</li><li>아주 멋진 설계의 API도 정말 필요하지 않으면 과유불급</li><li>좋은 API는 걸리적 거리지 않는다<br>→ 그래야 팀이 창의적 노력을 사용자 스토리에 집중<br>→ 그렇지 않으면 아키텍처에 발이 묶임 → 고객에게 최적의 가치를 효율적으로 제공 X</li></ul><blockquote><p>최선의 시스템 아키텍처는 각각 POJO 객체로 구현되는 모듈화된 관심사 영역(modularized domains of concern)(도메인)으로 구성된다. 이렇게 서로 다른 영역은 영역 코드 간섭을 최소화하는 Aspect나 유사 도구를 사용해 통합한다. <strong>이러한 아키텍쳐는 코드와 마찬가지로 test-driven될 수 있다.</strong></p></blockquote><h2 id="의사-결정을-최적화하라"><a href="#의사-결정을-최적화하라" class="headerlink" title="의사 결정을 최적화하라"></a>의사 결정을 최적화하라</h2><ul><li>도시든 sw시스템이든 아주 큰 시스템에서는 한 사람이 모든 결정을 내리기 어렵다</li><li>모듈화, 관심사 분리 → 지엽적 관리/결정 가능</li><li>가장 적합한 사람에게 책임을 맡겨라</li><li><strong>때때로 가능한 한 마지막 순간까지 결정을 미루는 방법이 최선</strong><ul><li>게으르거나 무책임해서가 아님</li><li>최대한 정보를 모아 최선의 결정을 내리기 위해서</li><li>성급한 결정 → 불충분한 지식을 기반으로 내린 결정</li><li>너무 이른 결정 → 국민 피드백 더 모으고, 프로젝트를 더 고민하고, 구현 방안을 더 탐험할 기회가 사라짐</li></ul></li></ul><blockquote><ul><li>관심사를 모듈로 분리한 POJO 시스템은 기민함을 제공</li><li>이런 기민함(민첩성)은 가장 최신의 정보에 기반하여 최선의 시점에 최적의 결정을 내리기 쉽게 해줌</li><li>결정에 필요한 복잡도 또한 경감</li></ul></blockquote><h2 id="명백한-가치가-있을-떄-표준을-현명하게-사용하라"><a href="#명백한-가치가-있을-떄-표준을-현명하게-사용하라" class="headerlink" title="명백한 가치가 있을 떄 표준을 현명하게 사용하라"></a>명백한 가치가 있을 떄 표준을 현명하게 사용하라</h2><p>현대 건축현장 : 엄청난 설계, 엄청난 속도, 엄청난 최적화된 표준<br>EJB2의 사례</p><ul><li>단지 표준이라는 이유만으로 많은 팀이 사용</li><li>가볍고 간단한 설계로 충분한 프로젝트도 EJB2 사용</li><li>과장되게 포장된 표준에 집착하여 고객 가치가 뒷전으로 밀려난 사례</li><li>“고객을 위한 가치 창출”이라는 목표를 표준에 심취해 잃어버림</li></ul><blockquote><p>표준은 아이디어와 컴포넌트의 재사용, 관련 전문가 채용, 좋은 아이디어의 캡슐화, 컴포넌트들의 연결을 쉽게 도와 준다. 하지만 때로는 표준을 만드는 데에 드는 시간이 너무 오래 걸려 납품 기한을 맞추기 어렵게 만들고, 혹은 어떤 표준은 최초에 제공하려던 목적과 가치를 제공하지 못한 표준도 있다</p></blockquote><h2 id="시스템에는-DSL-도메인-영역-언어-이-필요하다"><a href="#시스템에는-DSL-도메인-영역-언어-이-필요하다" class="headerlink" title="시스템에는 DSL(도메인 영역 언어)이 필요하다"></a>시스템에는 DSL(도메인 영역 언어)이 필요하다</h2><p>DSL</p><ul><li>간단한 스크립트 언어나 표준 언어로 구현한 API</li><li>DSL 코드는 도메인 전문가가 작성한 구조적인 산문처럼 읽힌다</li><li>의사소통 간극을 줄인다<ul><li>좋은 DSL은 도메인 개념과 그 개념을 구현한 코드 사이에 존재하는 ‘의사 소통 간극’을 줄인다</li><li>Agile 기법이 팀과 프로젝트 이해 관계자 사이의 소통 간극을 줄이듯이</li><li>모메인 전문가가 사용하는 언오로 도메인 논리 구현 → 도메인 잘못 구현할 가능성 감소</li></ul></li><li>효과적으로 사용한다면?<ul><li>추상화 수준을 코드 관용구나 디자인 패턴으로 올릴 수 있음</li><li>개발자가 적절한 추상화 수준에서 코드 의도를 표현 가능해짐</li></ul></li></ul><blockquote><p>DSL을 사용시 코차원 정책부터 저차원의 세부사항까지 모든 추상화 수준과 모든 도메인을 POJO로 표현 가능</p></blockquote><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li>(코드뿐만이 아니라) 시스템 역시 깨끗해야 한다</li><li>클린하지 못한 침략적인(invasive) 아키텍쳐는 도메인 논리를 흐리며 기민성을 떨어뜨림</li><li>도메인 논리 흐려지면 ? 제품 품질이 떨어지고, 버그는 숨기 쉬워지고 기능 구현은 어려워 진다</li><li>기민성이 떨어지면 침해되면 생산성이 저해되고 TDD가 제공하는 장점이 사라짐</li><li>모든 추상화 단계에서 의도는 명확히 표현해야 한다 그러려면<ul><li>POJO를 작성하고</li><li>aspect나 유사한 매커니즘을 사용해 각 구현 관심사를 분리해야 한다</li></ul></li><li>시스템을 설계하든, 개별 모듈을 설계하든, <strong>실제로 돌아가는 가장 단순한 수단</strong>을 사용해야 한다</li></ul><h2 id="Related-POST-1"><a href="#Related-POST-1" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch11/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅹ. 클래스</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch10/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch10/</guid>
      <pubDate>Wed, 09 Feb 2022 00:54:31 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅹ-클래스&quot;&gt;&lt;a href=&quot;#Ⅹ-클래스&quot; class=&quot;headerlink&quot; title=&quot;Ⅹ. 클래스&quot;&gt;&lt;/a&gt;Ⅹ. 클래스&lt;/h</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅹ-클래스"><a href="#Ⅹ-클래스" class="headerlink" title="Ⅹ. 클래스"></a>Ⅹ. 클래스</h1><blockquote><ul><li>앞서 코드 행과 코드 블록을 올바로 작성하는 방법, 함수를 올바로 구현하는 방법과 함수가 서로 관련을 맺는 방법 공부</li><li>위의 내용과 더불어 더 높은 단계까지 신경을 써야만 깨끗한 코드를 얻을 수 있음</li></ul></blockquote><h2 id="클래스-체계"><a href="#클래스-체계" class="headerlink" title="클래스 체계"></a>클래스 체계</h2><p>표준 자바 관례</p><ul><li>가장 먼저 변수 목록<ul><li>static, public 상수가 있을 경우 맨 처음에 나온다</li><li>그다음 정적 비공개, 이어서 비공개 인스턴스 변수</li><li>(공개 변수가 필요한 경우는 거의 없음)</li></ul></li><li>공개 함수</li><li>비공개 함수</li><li>왜 이렇게 ?<ul><li>가독성 좋게</li><li>추상화 단계가 순차적으로 내려간다</li><li>프로그램은 신문 기사처럼 읽힌다</li></ul></li></ul><h3 id="캡슐화"><a href="#캡슐화" class="headerlink" title="캡슐화"></a>캡슐화</h3><p>변수/유틸리티</p><ul><li>변수와 유틸리티 함수는 가능한 공개하지 않는 편이 낫지만 반드시 숨겨야 하는 것은 아니다.</li><li>protected로 선언해서 접근을 허용하기도 한다.<ul><li>예) 테스트</li><li>우리에게 테스트는 중요하다</li><li>같은 패키지안에서 테스트 코드가 함수 호출하거나 변수를 사용시</li><li>해당 함수나 변수를 protected로 선언하거나 패키지 전체로 공개</li></ul></li><li>캡슐화를 풀어주는 결정은 언제나 최후의 수단 :  비공개 상태를 유지할 온갖 방법을 강구</li></ul><h2 id="클래스는-작아야-한다"><a href="#클래스는-작아야-한다" class="headerlink" title="클래스는 작아야 한다"></a>클래스는 작아야 한다</h2><ul><li>함수장에서 했던 이야기의 되풀이가 아님 : 함수와 마찬가지로 “작게”가 기본 규칙이라는 의미</li><li>얼마나 작아야 하나?<figure class="highlight java"><figcaption><span>너무 많은 책임</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 어마어마하게 큰 슈퍼 만능 클래스</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SuperDashboard</span> <span class="keyword">extends</span> <span class="title class_">JFrame</span> <span class="keyword">implements</span> <span class="title class_">MetaDataUser</span> {</span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getCustomizerLanguagePath</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setSystemConfigPath</span><span class="params">(String systemConfigPath)</span> </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getSystemConfigDocument</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setSystemConfigDocument</span><span class="params">(String systemConfigDocument)</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">getGuruState</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">getNoviceState</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">getOpenSourceState</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">showObject</span><span class="params">(MetaObject object)</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">showProgress</span><span class="params">(String s)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isMetadataDirty</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setIsMetadataDirty</span><span class="params">(<span class="type">boolean</span> isMetadataDirty)</span></span><br><span class="line">  <span class="keyword">public</span> Component <span class="title function_">getLastFocusedComponent</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLastFocused</span><span class="params">(Component lastFocused)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMouseSelectState</span><span class="params">(<span class="type">boolean</span> isMouseSelected)</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isMouseSelected</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> LanguageManager <span class="title function_">getLanguageManager</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> Project <span class="title function_">getProject</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> Project <span class="title function_">getFirstProject</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> Project <span class="title function_">getLastProject</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getNewProjectName</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setComponentSizes</span><span class="params">(Dimension dim)</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getCurrentDir</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCurrentDir</span><span class="params">(String newDir)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateStatus</span><span class="params">(<span class="type">int</span> dotPos, <span class="type">int</span> markPos)</span></span><br><span class="line">  <span class="keyword">public</span> Class[] getDataBaseClasses()</span><br><span class="line">  <span class="keyword">public</span> MetadataFeeder <span class="title function_">getMetadataFeeder</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addProject</span><span class="params">(Project project)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">setCurrentProject</span><span class="params">(Project project)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">removeProject</span><span class="params">(Project project)</span></span><br><span class="line">  <span class="keyword">public</span> MetaProjectHeader <span class="title function_">getProgramMetadata</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">resetDashboard</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> Project <span class="title function_">loadProject</span><span class="params">(String fileName, String projectName)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setCanSaveMetadata</span><span class="params">(<span class="type">boolean</span> canSave)</span></span><br><span class="line">  <span class="keyword">public</span> MetaObject <span class="title function_">getSelectedObject</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">deselectObjects</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setProject</span><span class="params">(Project project)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">editorAction</span><span class="params">(String actionName, ActionEvent event)</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMode</span><span class="params">(<span class="type">int</span> mode)</span></span><br><span class="line">  <span class="keyword">public</span> FileManager <span class="title function_">getFileManager</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setFileManager</span><span class="params">(FileManager fileManager)</span></span><br><span class="line">  <span class="keyword">public</span> ConfigManager <span class="title function_">getConfigManager</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setConfigManager</span><span class="params">(ConfigManager configManager)</span> </span><br><span class="line">  <span class="keyword">public</span> ClassLoader <span class="title function_">getClassLoader</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setClassLoader</span><span class="params">(ClassLoader classLoader)</span></span><br><span class="line">  <span class="keyword">public</span> Properties <span class="title function_">getProps</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getUserHome</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">getBaseDir</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMajorVersionNumber</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMinorVersionNumber</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getBuildNumber</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> MetaObject <span class="title function_">pasting</span><span class="params">(MetaObject target, MetaObject pasted, MetaProject project)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processMenuItems</span><span class="params">(MetaObject metaObject)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processMenuSeparators</span><span class="params">(MetaObject metaObject)</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processTabPages</span><span class="params">(MetaObject metaObject)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processPlacement</span><span class="params">(MetaObject object)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processCreateLayout</span><span class="params">(MetaObject object)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateDisplayLayer</span><span class="params">(MetaObject object, <span class="type">int</span> layerIndex)</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">propertyEditedRepaint</span><span class="params">(MetaObject object)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processDeleteObject</span><span class="params">(MetaObject object)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">getAttachedToDesigner</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processProjectChangedState</span><span class="params">(<span class="type">boolean</span> hasProjectChanged)</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processObjectNameChanged</span><span class="params">(MetaObject object)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">runProject</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> setAçowDragging(<span class="type">boolean</span> allowDragging) </span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">allowDragging</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isCustomizing</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setTitle</span><span class="params">(String title)</span></span><br><span class="line">  <span class="keyword">public</span> IdeMenuBar <span class="title function_">getIdeMenuBar</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">showHelper</span><span class="params">(MetaObject metaObject, String propertyName)</span> </span><br><span class="line">  </span><br><span class="line">  <span class="comment">// ... 많은 비공개 메서드가 계속 이어짐...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><figure class="highlight java"><figcaption><span>메서드가 5개라면?</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 메서드 수가 작아도 책임이 너무 많다</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SuperDashboard</span> <span class="keyword">extends</span> <span class="title class_">JFrame</span> <span class="keyword">implements</span> <span class="title class_">MetaDataUser</span> {</span><br><span class="line">  <span class="keyword">public</span> Component <span class="title function_">getLastFocusedComponent</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLastFocused</span><span class="params">(Component lastFocused)</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMajorVersionNumber</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMinorVersionNumber</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getBuildNumber</span><span class="params">()</span> </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>클래스 이름은 해당 클래스 책임을 기술해야</p><ul><li>작명은 클래스 크기를 줄이는 첫번째 관문</li><li>간결한 이름이 떠오르지 않는다면? 클래스 책임이 너무 많아서!</li><li>클래스 이름에 Manager, Processor, Super등 모호한 단어(ch2언급)가 있다면 여러 책임을 떠안긴 것</li><li>클래스 설명은 “if”, “and”, “or”, “but”을 사용하지 않고 25 단어 내외로 가능해야</li><li>위의 SuperDashboard를 설명하면?<ul><li>“SuperDashboard는 마지막으로 포커스를 얻었던 컴포넌트에 접근하는 방법을 제공하며, 버전과 빌드 번호를 추적하는 메커니즘을 제공한다.”</li><li>첫 번쨰 “~하며,”는 SuperDashboard에 책임이 너무 많다는 증거</li></ul></li></ul><h3 id="단일-책임-원칙"><a href="#단일-책임-원칙" class="headerlink" title="단일 책임 원칙"></a>단일 책임 원칙</h3><blockquote><p>자세한 것은 클린 소프트웨어 참조<br>SRP</p></blockquote><ul><li>클래스나 모듈을 변경할 이유가 단 하나뿐이어야 한다는 원칙</li><li>“책임”이라는 개념 정의, 적절한 클래스 크기 제시</li></ul><p>위 5가지 메서드의 SuperDashboard는?</p><ol><li>sw 버전 정보를 추적 : sw 출시 때마다 달라짐</li><li>자바 스윙 컴포넌트 관리</li></ol><ul><li>최상위 GUI 윈도우 스윙 표현인 JFrame에서 파생한 클래스이므로</li><li>스윙 코드를 변경할 떄마다 버전 번호가 달라짐</li><li>역은 false : 때로는 다른 코드를 바꾸고 나서도 버전 번호를 바꾼다</li></ul><p>책임 = 변경할 이유</p><ul><li>이를 파악하려 애쓰면 코드 추상화가 쉬어진다</li><li>위에서 버전 정보를 다루는 메서드들을 빼내서 독자적인 클래스를 만든다면  <figure class="highlight java"><figcaption><span>단일 책임 클래스</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//다른 어플리케이션에서 재사용하기 아주 쉬운 구조!</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Version</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMajorVersionNumber</span><span class="params">()</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMinorVersionNumber</span><span class="params">()</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getBuildNumber</span><span class="params">()</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><p>SRP = 중요하고 이해하고 지키기 쉽지만 설계자가 잘 어기는 규칙 중 하나..  어째서?</p><ul><li><p>sw를 돌아가게 만드는 활동(구현 개발)과 sw를 깨끗하게 만드는 활동(클린 코드)은 별개</p></li><li><p>두뇌 용량의 한계로 클린 코딩 보다는 구현 개발에 초점</p><ul><li>올바른 태도</li><li>관심사를 분리하는 작업은 프로그램뿐 아닌 프로그래밍 활동에서도 마찬가지로 중요</li></ul></li><li><p>문제는?</p><ol><li>프로그램이 돌아가면 일이 끝났다고 여김</li></ol><ul><li>구현 개발의 관심사 종료시 ‘깨끗하고 체계적인 sw(클린 코드)’라는 다음 관심사로 전환하지 않음</li><li>구현된 만능 클래스를 단일 책임 클래스 여럿으로 분리하는 대신 다음 이슈처리해버림</li></ul><ol start="2"><li>클래스가 많아지면 큰 그림 이해 못한다고 우려</li></ol><ul><li>자잘한 SRP 클래스가 많아지면 큰 그림 이해를 위해 여러 클래스를 수없이 넘나들어야 한다며 걱정</li></ul></li><li><p>양은 비슷하다</p><ul><li>작은 클래스가 많은 시스템에든, 큰 클래스가 몇개 뿐인 시스템이든 들어가는 부품은 그 수가 비슷함</li><li>어느 시스템이든 익힐 내용은 그 양이 비슷</li></ul></li><li><p>따라서 고민할 질문은 <strong>도구상자를 어떻게 관리하고 싶은가?</strong> 이다</p><ul><li>작은 서랍을 많이 두고 기능과 이름이 명확한 컴포넌트를 나눠 넣고 싶은가?</li><li>큰 서러 몇개에 모두를 던져 넣고 싶은가?</li></ul></li><li><p>기본 규모가 어느정도에 이르는 시스템은 논리가 많고도 복잡하다</p></li><li><p>복잡성을 다룰려면 체계적인 정리가 필수</p></li><li><p>그래야 개발자가 어디에 무엇이 있는지 쉽게 찾을 수 있음</p></li><li><p>그래야 (변경시)직접 영향이 미치는 컴포넌트만 이해해도 충분</p></li><li><p>그렇지 않은 다목적 클래스 몇 개로 구성된 시스템은 (변경시) 당장 알 필요가 없는 부분까지 보여 독자 방해</p></li><li><p>작은 클래스 여럿으로 이뤄진 시스템이 바람직함<br><strong>작은 클래스는 각자 맡은 책임이 하나며, 변경할 이유가 하나며, 다른 작은 클래스와 협력해 시스템에 필요한 동작을 수행한다.</strong></p></li></ul><h3 id="응집도-Cohesion"><a href="#응집도-Cohesion" class="headerlink" title="응집도(Cohesion)"></a>응집도(Cohesion)</h3><ul><li>클래스는 인스턴스 변수 수가 작아야 한다.</li><li>각 클래스 메서드는 클래스 인스턴스 변수를 하나 이상 사용해야 한다.</li><li>일반적으로 메서드가 변수를 더 많이 사용할 수록 메서드와 클래스는 응집도가 더 높다.</li><li>모든 인스턴스 변수를 메서드마다 사용하는 클래스는 응집도가 가장 높지만, 이런 클래스는 가능하지도, 바람직하지도 않다.</li><li>하지만 가능한한 응집도가 높은 클래스를 지향해야 한다.</li><li>응집도가 높다 = 클래스에 속한 메서드와 변수가 서로 의존하며 논리적인 단위로 묶인다는 의미</li></ul><figure class="highlight java"><figcaption><span>응집도가 높은 Stack 구현 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//size를 제외한 두 메서드는 두 변수 모두 사용</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Stack</span> {</span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> <span class="variable">topOfStack</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">  List&lt;Integer&gt; elements = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;Integer&gt;();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> topOfStack;</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> element)</span> { </span><br><span class="line">    topOfStack++; </span><br><span class="line">    elements.add(element);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">pop</span><span class="params">()</span> <span class="keyword">throws</span> PoppedWhenEmpty { </span><br><span class="line">    <span class="keyword">if</span> (topOfStack == <span class="number">0</span>)</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">PoppedWhenEmpty</span>();</span><br><span class="line">    <span class="type">int</span> <span class="variable">element</span> <span class="operator">=</span> elements.get(--topOfStack); </span><br><span class="line">    elements.remove(topOfStack);</span><br><span class="line">    <span class="keyword">return</span> element;</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p><strong>함수를 작게, 매개변수 목록을 짧게</strong>라는 전략을 따르다 보면 때때로 몇몇 메서드만이 사용하는 인스턴스 변수가 아주 많아지는 케이스 발생<br>→ 이는 십중 팔구 새로운 클래스를 쪼개야 한다는 신호<br>→ 응집도가 높아지도록 변수와 메서드를 적절히 분리해 새로운 클래스 두세 개로 쪼갠다</p><h3 id="응집도를-유지하면-작은-클래스-여럿이-나온다"><a href="#응집도를-유지하면-작은-클래스-여럿이-나온다" class="headerlink" title="응집도를 유지하면 작은 클래스 여럿이 나온다"></a>응집도를 유지하면 작은 클래스 여럿이 나온다</h3><p><strong>큰 함수를 작은 함수 여럿으로 나누기만 해도 클래스 수가 많아진다</strong><br>예)변수가 아주 많은 큰 함수</p><ul><li>큰 함수 일부를 작은 함수로 extract하려 함</li><li>빼내려는 코드가 큰 함수에 정의 된 변수를 많이 사용한다</li><li>그러면 변수들을 새 함수에 인수로 넘겨야 하나?</li><li>답은? 전혀 아니다<ul><li>변수들을 클래스 인스턴스 변수로 승격 시키면 인수가 필요없음</li><li>불행히 응집력이 낮아짐 : 몇몇 함수만 사용하는 인스턴스 변수가 점점 더 늘어나므로</li><li>몇몇 함수가 몇몇 인스턴스 변수만 사용하게되면? 독자적인 클래스로 쪼개라!!</li></ul></li></ul><p>결론 : 큰 함수 작은 함수로 스플릿 → 종종 작은 클래스 여럿으로 쪼갤 기회 → 프로그램의 체계가 잡히고 구조가 투명해짐</p><p>예시) 커누스 교수가 쓴 문학적 프로그래밍(Literate Programming)에 나오는 유서 깊은 예제</p><ul><li>커누스 교수의 PrintPrimes를 자바로 변환한 코드</li><li>그가 직접 짠 포르그램이 아닌 웹도구로 출력한 결과</li></ul><figure class="highlight java"><figcaption><span>PrintPrimes.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> literatePrimes;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PrintPrimes</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">M</span> <span class="operator">=</span> <span class="number">1000</span>; </span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">RR</span> <span class="operator">=</span> <span class="number">50</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">CC</span> <span class="operator">=</span> <span class="number">4</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">WW</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">ORDMAX</span> <span class="operator">=</span> <span class="number">30</span>; </span><br><span class="line">    <span class="type">int</span> P[] = <span class="keyword">new</span> <span class="title class_">int</span>[M + <span class="number">1</span>]; </span><br><span class="line">    <span class="type">int</span> PAGENUMBER;</span><br><span class="line">    <span class="type">int</span> PAGEOFFSET; </span><br><span class="line">    <span class="type">int</span> ROWOFFSET; </span><br><span class="line">    <span class="type">int</span> C;</span><br><span class="line">    <span class="type">int</span> J;</span><br><span class="line">    <span class="type">int</span> K;</span><br><span class="line">    <span class="type">boolean</span> JPRIME;</span><br><span class="line">    <span class="type">int</span> ORD;</span><br><span class="line">    <span class="type">int</span> SQUARE;</span><br><span class="line">    <span class="type">int</span> N;</span><br><span class="line">    <span class="type">int</span> MULT[] = <span class="keyword">new</span> <span class="title class_">int</span>[ORDMAX + <span class="number">1</span>];</span><br><span class="line">    </span><br><span class="line">    J = <span class="number">1</span>;</span><br><span class="line">    K = <span class="number">1</span>; </span><br><span class="line">    P[<span class="number">1</span>] = <span class="number">2</span>; </span><br><span class="line">    ORD = <span class="number">2</span>; </span><br><span class="line">    SQUARE = <span class="number">9</span>;</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">while</span> (K &lt; M) { </span><br><span class="line">      <span class="keyword">do</span> {</span><br><span class="line">        J = J + <span class="number">2</span>;</span><br><span class="line">        <span class="keyword">if</span> (J == SQUARE) {</span><br><span class="line">          ORD = ORD + <span class="number">1</span>;</span><br><span class="line">          SQUARE = P[ORD] * P[ORD]; </span><br><span class="line">          MULT[ORD - <span class="number">1</span>] = J;</span><br><span class="line">        }</span><br><span class="line">        N = <span class="number">2</span>;</span><br><span class="line">        JPRIME = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">while</span> (N &lt; ORD &amp;&amp; JPRIME) {</span><br><span class="line">          <span class="keyword">while</span> (MULT[N] &lt; J)</span><br><span class="line">            MULT[N] = MULT[N] + P[N] + P[N];</span><br><span class="line">          <span class="keyword">if</span> (MULT[N] == J) </span><br><span class="line">            JPRIME = <span class="literal">false</span>;</span><br><span class="line">          N = N + <span class="number">1</span>; </span><br><span class="line">        }</span><br><span class="line">      } <span class="keyword">while</span> (!JPRIME); </span><br><span class="line">      K = K + <span class="number">1</span>;</span><br><span class="line">      P[K] = J;</span><br><span class="line">    } </span><br><span class="line">    {</span><br><span class="line">      PAGENUMBER = <span class="number">1</span>; </span><br><span class="line">      PAGEOFFSET = <span class="number">1</span>;</span><br><span class="line">      <span class="keyword">while</span> (PAGEOFFSET &lt;= M) {</span><br><span class="line">        System.out.println(<span class="string">"The First "</span> + M + <span class="string">" Prime Numbers --- Page "</span> + PAGENUMBER);</span><br><span class="line">        System.out.println(<span class="string">""</span>);</span><br><span class="line">        <span class="keyword">for</span> (ROWOFFSET = PAGEOFFSET; ROWOFFSET &lt; PAGEOFFSET + RR; ROWOFFSET++) {</span><br><span class="line">          <span class="keyword">for</span> (C = <span class="number">0</span>; C &lt; CC;C++)</span><br><span class="line">            <span class="keyword">if</span> (ROWOFFSET + C * RR &lt;= M)</span><br><span class="line">              System.out.format(<span class="string">"%10d"</span>, P[ROWOFFSET + C * RR]); </span><br><span class="line">          System.out.println(<span class="string">""</span>);</span><br><span class="line">        }</span><br><span class="line">        System.out.println(<span class="string">"\f"</span>); PAGENUMBER = PAGENUMBER + <span class="number">1</span>; PAGEOFFSET = PAGEOFFSET + RR * CC;</span><br><span class="line">      }</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>엉망징창 프로그램</p><ul><li>심한 들여쓰기</li><li>많은 이상한 변수</li><li>빡빡히 결합된 구조</li></ul><p>작은 함수와 클래스로 나누고 함수와 클래스와 변수에 좀더 의미있는 이름을 부여하며 리팩터링을 해보자</p><figure class="highlight java"><figcaption><span>PrimePrinter.java (리팩터링 버전)</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> literatePrimes;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PrimePrinter</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">NUMBER_OF_PRIMES</span> <span class="operator">=</span> <span class="number">1000</span>;</span><br><span class="line">    <span class="type">int</span>[] primes = PrimeGenerator.generate(NUMBER_OF_PRIMES);</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">ROWS_PER_PAGE</span> <span class="operator">=</span> <span class="number">50</span>; </span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">COLUMNS_PER_PAGE</span> <span class="operator">=</span> <span class="number">4</span>; </span><br><span class="line">    <span class="type">RowColumnPagePrinter</span> <span class="variable">tablePrinter</span> <span class="operator">=</span> </span><br><span class="line">      <span class="keyword">new</span> <span class="title class_">RowColumnPagePrinter</span>(ROWS_PER_PAGE, </span><br><span class="line">            COLUMNS_PER_PAGE, </span><br><span class="line">            <span class="string">"The First "</span> + NUMBER_OF_PRIMES + <span class="string">" Prime Numbers"</span>);</span><br><span class="line">    tablePrinter.print(primes); </span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>RowColumnPagePrinter.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> literatePrimes;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.PrintStream;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RowColumnPagePrinter</span> { </span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> rowsPerPage;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> columnsPerPage; </span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> numbersPerPage; </span><br><span class="line">  <span class="keyword">private</span> String pageHeader; </span><br><span class="line">  <span class="keyword">private</span> PrintStream printStream;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">RowColumnPagePrinter</span><span class="params">(<span class="type">int</span> rowsPerPage, <span class="type">int</span> columnsPerPage, String pageHeader)</span> { </span><br><span class="line">    <span class="built_in">this</span>.rowsPerPage = rowsPerPage;</span><br><span class="line">    <span class="built_in">this</span>.columnsPerPage = columnsPerPage; </span><br><span class="line">    <span class="built_in">this</span>.pageHeader = pageHeader;</span><br><span class="line">    numbersPerPage = rowsPerPage * columnsPerPage; </span><br><span class="line">    printStream = System.out;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(<span class="type">int</span> data[])</span> { </span><br><span class="line">    <span class="type">int</span> <span class="variable">pageNumber</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">firstIndexOnPage</span> <span class="operator">=</span> <span class="number">0</span> ; </span><br><span class="line">      firstIndexOnPage &lt; data.length ; </span><br><span class="line">      firstIndexOnPage += numbersPerPage) { </span><br><span class="line">      <span class="type">int</span> <span class="variable">lastIndexOnPage</span> <span class="operator">=</span>  Math.min(firstIndexOnPage + numbersPerPage - <span class="number">1</span>, data.length - <span class="number">1</span>);</span><br><span class="line">      printPageHeader(pageHeader, pageNumber); </span><br><span class="line">      printPage(firstIndexOnPage, lastIndexOnPage, data); </span><br><span class="line">      printStream.println(<span class="string">"\f"</span>);</span><br><span class="line">      pageNumber++;</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">printPage</span><span class="params">(<span class="type">int</span> firstIndexOnPage, <span class="type">int</span> lastIndexOnPage, <span class="type">int</span>[] data)</span> { </span><br><span class="line">    <span class="type">int</span> <span class="variable">firstIndexOfLastRowOnPage</span> <span class="operator">=</span></span><br><span class="line">    firstIndexOnPage + rowsPerPage - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">firstIndexInRow</span> <span class="operator">=</span> firstIndexOnPage ; </span><br><span class="line">      firstIndexInRow &lt;= firstIndexOfLastRowOnPage ;</span><br><span class="line">      firstIndexInRow++) { </span><br><span class="line">      printRow(firstIndexInRow, lastIndexOnPage, data); </span><br><span class="line">      printStream.println(<span class="string">""</span>);</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">printRow</span><span class="params">(<span class="type">int</span> firstIndexInRow, <span class="type">int</span> lastIndexOnPage, <span class="type">int</span>[] data)</span> {</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">column</span> <span class="operator">=</span> <span class="number">0</span>; column &lt; columnsPerPage; column++) {</span><br><span class="line">      <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> firstIndexInRow + column * rowsPerPage; </span><br><span class="line">      <span class="keyword">if</span> (index &lt;= lastIndexOnPage)</span><br><span class="line">        printStream.format(<span class="string">"%10d"</span>, data[index]); </span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">printPageHeader</span><span class="params">(String pageHeader, <span class="type">int</span> pageNumber)</span> {</span><br><span class="line">    printStream.println(pageHeader + <span class="string">" --- Page "</span> + pageNumber);</span><br><span class="line">    printStream.println(<span class="string">""</span>); </span><br><span class="line">  }</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setOutput</span><span class="params">(PrintStream printStream)</span> { </span><br><span class="line">    <span class="built_in">this</span>.printStream = printStream;</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>PrimeGenerator.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> literatePrimes;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PrimeGenerator</span> {</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span>[] primes;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> ArrayList&lt;Integer&gt; multiplesOfPrimeFactors;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">static</span> <span class="type">int</span>[] generate(<span class="type">int</span> n) {</span><br><span class="line">    primes = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">    multiplesOfPrimeFactors = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Integer&gt;(); </span><br><span class="line">    set2AsFirstPrime(); </span><br><span class="line">    checkOddNumbersForSubsequentPrimes();</span><br><span class="line">    <span class="keyword">return</span> primes; </span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">set2AsFirstPrime</span><span class="params">()</span> { </span><br><span class="line">    primes[<span class="number">0</span>] = <span class="number">2</span>; </span><br><span class="line">    multiplesOfPrimeFactors.add(<span class="number">2</span>);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">checkOddNumbersForSubsequentPrimes</span><span class="params">()</span> { </span><br><span class="line">    <span class="type">int</span> <span class="variable">primeIndex</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">candidate</span> <span class="operator">=</span> <span class="number">3</span> ; primeIndex &lt; primes.length ; candidate += <span class="number">2</span>) { </span><br><span class="line">      <span class="keyword">if</span> (isPrime(candidate))</span><br><span class="line">        primes[primeIndex++] = candidate; </span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">isPrime</span><span class="params">(<span class="type">int</span> candidate)</span> {</span><br><span class="line">    <span class="keyword">if</span> (isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate)) {</span><br><span class="line">      multiplesOfPrimeFactors.add(candidate);</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> isNotMultipleOfAnyPreviousPrimeFactor(candidate); </span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">isLeastRelevantMultipleOfNextLargerPrimeFactor</span><span class="params">(<span class="type">int</span> candidate)</span> {</span><br><span class="line">    <span class="type">int</span> <span class="variable">nextLargerPrimeFactor</span> <span class="operator">=</span> primes[multiplesOfPrimeFactors.size()];</span><br><span class="line">    <span class="type">int</span> <span class="variable">leastRelevantMultiple</span> <span class="operator">=</span> nextLargerPrimeFactor * nextLargerPrimeFactor; </span><br><span class="line">    <span class="keyword">return</span> candidate == leastRelevantMultiple;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">isNotMultipleOfAnyPreviousPrimeFactor</span><span class="params">(<span class="type">int</span> candidate)</span> {</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">1</span>; n &lt; multiplesOfPrimeFactors.size(); n++) {</span><br><span class="line">      <span class="keyword">if</span> (isMultipleOfNthPrimeFactor(candidate, n)) </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">isMultipleOfNthPrimeFactor</span><span class="params">(<span class="type">int</span> candidate, <span class="type">int</span> n)</span> {</span><br><span class="line">    <span class="keyword">return</span> candidate == smallestOddNthMultipleNotLessThanCandidate(candidate, n);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">smallestOddNthMultipleNotLessThanCandidate</span><span class="params">(<span class="type">int</span> candidate, <span class="type">int</span> n)</span> {</span><br><span class="line">    <span class="type">int</span> <span class="variable">multiple</span> <span class="operator">=</span> multiplesOfPrimeFactors.get(n); </span><br><span class="line">    <span class="keyword">while</span> (multiple &lt; candidate)</span><br><span class="line">      multiple += <span class="number">2</span> * primes[n]; </span><br><span class="line">    multiplesOfPrimeFactors.set(n, multiple); </span><br><span class="line">    <span class="keyword">return</span> multiple;</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>변화</p><ol><li>프로그램이 길어짐</li></ol><ul><li>리팩터링 → 더 길 서술적인 변수명 사용</li><li>리팩터링 → 코드 주석을 추가하는 수단으로 함수 선언과 클래스 선언 사용<br> → 가독성을 높이고자 공백을 추가하고 포맷팅</li></ul><ol start="2"><li>책임에 따라 분류</li></ol><ul><li>PrimePrinter : main 함수 하나만 포함하며 실행 환경 책임</li><li>RowColumnPagePrinter : 숫자 목록을 주어진 행과 열에 맞춰 페이지에 출력하는 방법</li><li>PrimeGenerator : 소수 목록 생성<ul><li>객체로 인스턴스화하는 클래스가 아니다</li><li>변수를 선언하고 감추려고 사용하는 유용한 공간일 뿐</li><li>소수 계산 알고리즘 변경시 해당 클래스 변경</li></ul></li></ul><ol start="3"><li>재구현이 아니다</li></ol><ul><li>실제 동작원리가 비슷하다</li><li>원래 프로그램의 정확한 동작을 검증하는 테스트 슈트를 먼저 작성</li><li>이후 테스트를 수행하며 확인하면서 조금씩 코드를 변경</li></ul><h2 id="변경하기-쉬운-클래스"><a href="#변경하기-쉬운-클래스" class="headerlink" title="변경하기 쉬운 클래스"></a>변경하기 쉬운 클래스</h2><ul><li>시스템은 지속적인 변경 가해짐</li><li>변경이 있을 때 마다 의도대로 동작하지 않을 위험이 따른다.</li><li>깨끗한 시스템은 클래스를 체계적으로 관리해 변경에 따르는 위험을 최대한 낮춘다.<figure class="highlight java"><figcaption><span>변경이 필요해 손대야 하는 클래스</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 새로운 새로운 SQL문을 지원할 때, 기존 SQL문을 수정할 때 수정 필요</span></span><br><span class="line"><span class="comment">// 변경할 이유가 2가지 → SRP를 위반한다</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sql</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">Sql</span><span class="params">(String table, Column[] columns)</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">create</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">insert</span><span class="params">(Object[] fields)</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">selectAll</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">findByKey</span><span class="params">(String keyColumn, String keyValue)</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">select</span><span class="params">(Column column, String pattern)</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">select</span><span class="params">(Criteria criteria)</span></span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">preparedInsert</span><span class="params">()</span></span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">columnList</span><span class="params">(Column[] columns)</span></span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">valuesList</span><span class="params">(Object[] fields, <span class="keyword">final</span> Column[] columns)</span> <span class="keyword">private</span> String <span class="title function_">selectWithCriteria</span><span class="params">(String criteria)</span></span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">placeholderList</span><span class="params">(Column[] columns)</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>(경험에 의하면) 클래스 일부에서만 사용되는 비공개 메서드는 코드를 개선할 잠재젹 여지 시사<br>→ 실제로 개선에 뛰어드는 계기는 시스템이 변해서라야 한다<br>→ SQL클래스를 논리적으로 완성으로 여긴다면 책임 분리 시도 필요 없음<br>→ 변경이 필요하지 않는다면 내버려두는 편이 좋음<br>→ 변경을 위해 손을 대는 순간 설계 개선에 대한 고민/시도 필요<figure class="highlight java"><figcaption><span>닫힌 클래스 집합</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 공개 인터페이스를 전부 SQL 클래스에서 파생하는 클래스로 만듬</span></span><br><span class="line"><span class="comment">// valueList와 같은 비공개 메서드는 해당 파생 클래스로 옮김</span></span><br><span class="line"><span class="comment">// 모든 파생 클래스가 공통으로 사용하는 비공개 메서드는 Where, ColumnList 두 유틸 클래스에 넣었다</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">abstract</span> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sql</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Sql</span><span class="params">(String table, Column[] columns)</span> </span><br><span class="line">    <span class="keyword">abstract</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span>;</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CreateSql</span> <span class="keyword">extends</span> <span class="title class_">Sql</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CreateSql</span><span class="params">(String table, Column[] columns)</span> </span><br><span class="line">    <span class="meta">@Override</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SelectSql</span> <span class="keyword">extends</span> <span class="title class_">Sql</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SelectSql</span><span class="params">(String table, Column[] columns)</span> </span><br><span class="line">    <span class="meta">@Override</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InsertSql</span> <span class="keyword">extends</span> <span class="title class_">Sql</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">InsertSql</span><span class="params">(String table, Column[] columns, Object[] fields)</span> </span><br><span class="line">    <span class="meta">@Override</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">valuesList</span><span class="params">(Object[] fields, <span class="keyword">final</span> Column[] columns)</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SelectWithCriteriaSql</span> <span class="keyword">extends</span> <span class="title class_">Sql</span> { </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SelectWithCriteriaSql</span><span class="params">(</span></span><br><span class="line"><span class="params">    String table, Column[] columns, Criteria criteria)</span> </span><br><span class="line">    <span class="meta">@Override</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SelectWithMatchSql</span> <span class="keyword">extends</span> <span class="title class_">Sql</span> { </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SelectWithMatchSql</span><span class="params">(String table, Column[] columns, Column column, String pattern)</span> </span><br><span class="line">    <span class="meta">@Override</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FindByKeySql</span> <span class="keyword">extends</span> <span class="title class_">Sql</span> <span class="keyword">public</span> <span class="title function_">FindByKeySql</span><span class="params">(</span></span><br><span class="line"><span class="params">    String table, Column[] columns, String keyColumn, String keyValue)</span> </span><br><span class="line">    <span class="meta">@Override</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PreparedInsertSql</span> <span class="keyword">extends</span> <span class="title class_">Sql</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">PreparedInsertSql</span><span class="params">(String table, Column[] columns)</span> </span><br><span class="line">    <span class="meta">@Override</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">private</span> String <span class="title function_">placeholderList</span><span class="params">(Column[] columns)</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Where</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Where</span><span class="params">(String criteria)</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ColumnList</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ColumnList</span><span class="params">(Column[] columns)</span> <span class="keyword">public</span> String <span class="title function_">generate</span><span class="params">()</span></span><br><span class="line">  }</span><br></pre></td></tr></tbody></table></figure>특징 : 장점만 취한다</li><li>SRP 지원 : 클래스가 서로 분리됨, 함수 하나를 수정해도 다른 함수가 망가지지 않음</li><li>OCP 지원<ul><li>파생 클래스 생성하는 방식으로 새로운 기능 추가에 개방적<ul><li>update 문 추가시 UpdateSql 클래스를 끼워 넣으면 끝</li></ul></li><li>다른 클래스를 닫아놓음 → 수정에 폐쇄적</li></ul></li></ul><h3 id="변경으로부터-격리"><a href="#변경으로부터-격리" class="headerlink" title="변경으로부터 격리"></a>변경으로부터 격리</h3><ul><li>요구사항은 변한다 → 코드도 변한다</li><li>의존객체의 상세 구현에 의존하는 클라이언트 → 구현이 바뀌면 위험 → 인터페이스, 추상 클래스로 구현이 미치는 영향을 격리</li><li>상세한 구현에 의존하는 코드는 테스트도 어렵다</li><li>추상화를 통해 테스트가 가능할 정도로 시스템의 결합도를 낮춤→ 유연성과 재사용성도 더욱 높아진다.</li></ul><p>결함도가 낮다는 말은 각 시스템 요소가 다른 요소로부터 그리고 변경으로부터 잘 격리되어있다는 뜻이다.</p><p>예)Portfolio 클래스</p><ul><li>외부 TokyoStockExchange API를 사용해 포트폴리오 값을 계산 → 외부 시세 변화 영향으로 값이 달라짐</li></ul><figure class="highlight java"><figcaption><span>인터페이스를 생성한 후 메서드를 선언하다.</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">StockExchange</span> { </span><br><span class="line">  Money <span class="title function_">currentPrice</span><span class="params">(String symbol)</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// TokyoStockExchange 클래스 : StockExchange 인터페이스를 구현</span></span><br><span class="line"><span class="comment">// Portfolio 생성자를 통해 StockExchange 참조자를 주입</span></span><br><span class="line"><span class="keyword">public</span> Portfolio {</span><br><span class="line">  <span class="keyword">private</span> StockExchange exchange;</span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">Portfolio</span><span class="params">(StockExchange exchange)</span> {</span><br><span class="line">    <span class="built_in">this</span>.exchange = exchange; </span><br><span class="line">  }</span><br><span class="line">  <span class="comment">// ... </span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// StockExchange 인터페이스로 상세 구현의 의존을 막았으므로 테스트용 클래스로 바꿀 수 있음</span></span><br><span class="line"><span class="comment">// TokyoStockExchange 클래스를 흉내냄며 고정된 주가를 반환하는 테스트용 클래스 FixedStockExchangeStub 를 만들 수 있다</span></span><br><span class="line"><span class="comment">// 무난히 테스트 코드를 작성 할 수 있다.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PortfolioTest</span> {</span><br><span class="line">  <span class="keyword">private</span> FixedStockExchangeStub exchange;</span><br><span class="line">  <span class="keyword">private</span> Portfolio portfolio;</span><br><span class="line">  </span><br><span class="line">  <span class="meta">@Before</span></span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">setUp</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    exchange = <span class="keyword">new</span> <span class="title class_">FixedStockExchangeStub</span>(); </span><br><span class="line">    exchange.fix(<span class="string">"MSFT"</span>, <span class="number">100</span>);</span><br><span class="line">    portfolio = <span class="keyword">new</span> <span class="title class_">Portfolio</span>(exchange);</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="meta">@Test</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">GivenFiveMSFTTotalShouldBe500</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    portfolio.add(<span class="number">5</span>, <span class="string">"MSFT"</span>);</span><br><span class="line">    Assert.assertEquals(<span class="number">500</span>, portfolio.value()); </span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>위의 테스트가 가능할 정도로 시스템 결합을 낮추면?</p><ul><li>유연성과 재사용성이 더 높아짐</li><li>시스템 결합이 낮다 : 각 시스템 요소가 다른 요소, 그리고 변경으로부터 잘 격리가 되어있다는 의미 → 각 요소 이해도 쉬어짐</li><li>결합도 최소로 줄이면? 자연스럽게 DIP를 따르는 클래스가 나온다</li><li>DIP : 클래스가 상세 구현이 아닌 추상화 의존해야 한다는 원칙</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch10/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅸ. 단위 테스트</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch9/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch9/</guid>
      <pubDate>Wed, 09 Feb 2022 00:54:04 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅸ-단위-테스트&quot;&gt;&lt;a href=&quot;#Ⅸ-단위-테스트&quot; class=&quot;headerlink&quot; title=&quot;Ⅸ. 단위 테스트&quot;&gt;&lt;/a&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅸ-단위-테스트"><a href="#Ⅸ-단위-테스트" class="headerlink" title="Ⅸ. 단위 테스트"></a>Ⅸ. 단위 테스트</h1><p>1997이전</p><ul><li>TDD 개념 없었음</li><li>단위 테스트<ul><li>자기 프로그램이 돌아간다는 사실만 확인하는 일회성 코드</li><li>클래스 메서드를 공들여 구현한 후, 임시 코드를 급조해 테스트 수행</li></ul></li></ul><p>현재</p><ul><li>테스트 코드 : 코드의 구현을 모두 확인하는 테스트 코드</li><li>애자일과 TDD → 단위테스트 자동화 이미 많아짐</li><li>급한 테스트 추가 + 제대로 된 테스트 케이스 작성을 놓침</li></ul><h2 id="TDD-법칙-3가지"><a href="#TDD-법칙-3가지" class="headerlink" title="TDD 법칙 3가지"></a>TDD 법칙 3가지</h2><p>3가지 법칙</p><ol><li>실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다</li><li>컵파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트 작성</li><li>현재 실패하는 테스트를 통과할 정도로만 실제 코드 작성<br>→ 3법칙 따르면 개발과 테스트가 대략 30초 주기<br>→ 테스트 코드가 실제 코드보다 불과 몇 초전<br>→ 매일 수십개, 매달 수백개, 매년 수천 개에 해당하는 테스트 케이스<br>→ 실제 코드를 사실상 전부 테스트 하는 테스트 케이스<br>→ 때로는 심각한 관리 무제 유발</li></ol><h2 id="깨끗한-코드-유지하기"><a href="#깨끗한-코드-유지하기" class="headerlink" title="깨끗한 코드 유지하기"></a>깨끗한 코드 유지하기</h2><p>테스트 코드는 지저분해도 빨리?</p><ul><li>실제 코드 진화 → 테스트도 진화 필요</li><li>테스트 코드가 지저분하면 변경이 어려움 → 테스트 코드 추가/수정이 실제 코드 짜는 시간보다 오래걸림</li><li><strong>테스트 코드는 실제 코드 못지 않게 중요하다</strong></li><li>테스트 코드도 구현 코드처럼 깨끗하게 짜야 한다</li></ul><h3 id="테스트는-유연성-유지보수성-재사용성-제공"><a href="#테스트는-유연성-유지보수성-재사용성-제공" class="headerlink" title="테스트는 유연성, 유지보수성, 재사용성 제공"></a>테스트는 유연성, 유지보수성, 재사용성 제공</h3><ul><li>테스트 케이스가 있으면 변경에 대한 공포없이 안심하고 개선 가능</li><li>테스트는 변경되는 설계와 아키텍처를 최대한 깨끗하게 보존하는 열쇠</li></ul><h2 id="Clean-Test-Code"><a href="#Clean-Test-Code" class="headerlink" title="Clean Test Code"></a>Clean Test Code</h2><ul><li>테스트 코드의 가장 중요한 점? 가독성</li><li>실제코드보다 테스트 코드에서 가독성은 더 중요</li><li>높은 가독성? → 명료성, 단순성, 붕푸한 표현력</li></ul><figure class="highlight java"><figcaption><span>SerializedPageResponderTest.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// FitNess에서 가져온 코드</span></span><br><span class="line"> <span class="comment">// 아래 테스트 케이스 세 개는 이해하기 어렵기에 개선할 여지가 충분</span></span><br><span class="line"> <span class="comment">// 첫째, addPage와 assertSubString을 부르느라 중복되는 코드가 매우 많다</span></span><br><span class="line"> <span class="comment">// 좀 더 중요하게는 자질구레한 사항이 너무 많아 테스트 코드의 표현력이 떨어진다.</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetPageHieratchyAsXml</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  crawler.addPage(root, PathParser.parse(<span class="string">"PageOne"</span>));</span><br><span class="line">  crawler.addPage(root, PathParser.parse(<span class="string">"PageOne.ChildOne"</span>));</span><br><span class="line">  crawler.addPage(root, PathParser.parse(<span class="string">"PageTwo"</span>));</span><br><span class="line"></span><br><span class="line">  request.setResource(<span class="string">"root"</span>);</span><br><span class="line">  request.addInput(<span class="string">"type"</span>, <span class="string">"pages"</span>);</span><br><span class="line">  <span class="type">Responder</span> <span class="variable">responder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SerializedPageResponder</span>();</span><br><span class="line">  <span class="type">SimpleResponse</span> <span class="variable">response</span> <span class="operator">=</span></span><br><span class="line">    (SimpleResponse) responder.makeResponse(<span class="keyword">new</span> <span class="title class_">FitNesseContext</span>(root), request);</span><br><span class="line">  <span class="type">String</span> <span class="variable">xml</span> <span class="operator">=</span> response.getContent();</span><br><span class="line"></span><br><span class="line">  assertEquals(<span class="string">"text/xml"</span>, response.getContentType());</span><br><span class="line">  assertSubString(<span class="string">"&lt;name&gt;PageOne&lt;/name&gt;"</span>, xml);</span><br><span class="line">  assertSubString(<span class="string">"&lt;name&gt;PageTwo&lt;/name&gt;"</span>, xml);</span><br><span class="line">  assertSubString(<span class="string">"&lt;name&gt;ChildOne&lt;/name&gt;"</span>, xml);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetPageHieratchyAsXmlDoesntContainSymbolicLinks</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  <span class="type">WikiPage</span> <span class="variable">pageOne</span> <span class="operator">=</span> crawler.addPage(root, PathParser.parse(<span class="string">"PageOne"</span>));</span><br><span class="line">  crawler.addPage(root, PathParser.parse(<span class="string">"PageOne.ChildOne"</span>));</span><br><span class="line">  crawler.addPage(root, PathParser.parse(<span class="string">"PageTwo"</span>));</span><br><span class="line"></span><br><span class="line">  <span class="type">PageData</span> <span class="variable">data</span> <span class="operator">=</span> pageOne.getData();</span><br><span class="line">  <span class="type">WikiPageProperties</span> <span class="variable">properties</span> <span class="operator">=</span> data.getProperties();</span><br><span class="line">  <span class="type">WikiPageProperty</span> <span class="variable">symLinks</span> <span class="operator">=</span> properties.set(SymbolicPage.PROPERTY_NAME);</span><br><span class="line">  symLinks.set(<span class="string">"SymPage"</span>, <span class="string">"PageTwo"</span>);</span><br><span class="line">  pageOne.commit(data);</span><br><span class="line"></span><br><span class="line">  request.setResource(<span class="string">"root"</span>);</span><br><span class="line">  request.addInput(<span class="string">"type"</span>, <span class="string">"pages"</span>);</span><br><span class="line">  <span class="type">Responder</span> <span class="variable">responder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SerializedPageResponder</span>();</span><br><span class="line">  <span class="type">SimpleResponse</span> <span class="variable">response</span> <span class="operator">=</span></span><br><span class="line">    (SimpleResponse) responder.makeResponse(<span class="keyword">new</span> <span class="title class_">FitNesseContext</span>(root), request);</span><br><span class="line">  <span class="type">String</span> <span class="variable">xml</span> <span class="operator">=</span> response.getContent();</span><br><span class="line"></span><br><span class="line">  assertEquals(<span class="string">"text/xml"</span>, response.getContentType());</span><br><span class="line">  assertSubString(<span class="string">"&lt;name&gt;PageOne&lt;/name&gt;"</span>, xml);</span><br><span class="line">  assertSubString(<span class="string">"&lt;name&gt;PageTwo&lt;/name&gt;"</span>, xml);</span><br><span class="line">  assertSubString(<span class="string">"&lt;name&gt;ChildOne&lt;/name&gt;"</span>, xml);</span><br><span class="line">  assertNotSubString(<span class="string">"SymPage"</span>, xml);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetDataAsHtml</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  crawler.addPage(root, PathParser.parse(<span class="string">"TestPageOne"</span>), <span class="string">"test page"</span>);</span><br><span class="line"></span><br><span class="line">  request.setResource(<span class="string">"TestPageOne"</span>); request.addInput(<span class="string">"type"</span>, <span class="string">"data"</span>);</span><br><span class="line">  <span class="type">Responder</span> <span class="variable">responder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SerializedPageResponder</span>();</span><br><span class="line">  <span class="type">SimpleResponse</span> <span class="variable">response</span> <span class="operator">=</span></span><br><span class="line">    (SimpleResponse) responder.makeResponse(<span class="keyword">new</span> <span class="title class_">FitNesseContext</span>(root), request);</span><br><span class="line">  <span class="type">String</span> <span class="variable">xml</span> <span class="operator">=</span> response.getContent();</span><br><span class="line"></span><br><span class="line">  assertEquals(<span class="string">"text/xml"</span>, response.getContentType());</span><br><span class="line">  assertSubString(<span class="string">"test page"</span>, xml);</span><br><span class="line">  assertSubString(<span class="string">"&lt;Test"</span>, xml);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>문제점 </p><ul><li>addPage와 assertSubString을 부르느라 중복되는 코드가 매우 많다</li><li><code>PathParser</code><ul><li>문자열을 pagePath 인스턴스로 변환</li><li>pagePath 웹 로봇(크롤러)가 사용하는 객체 → 테스트와 무관하며 테스트 의도만 흐린다</li></ul></li><li>Responder생성, respoonse 수집 코드 → 역시 잡음</li><li>resource의 인수에서 요청 URL 을 만드는 어설픈 코드</li><li><strong>읽는 사람을 고려하지 않음</strong></li></ul><figure class="highlight java"><figcaption><span>SerializedPageResponderTest.java 9-2_리팩터링한 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetPageHierarchyAsXml</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  makePages(<span class="string">"PageOne"</span>, <span class="string">"PageOne.ChildOne"</span>, <span class="string">"PageTwo"</span>);</span><br><span class="line"></span><br><span class="line">  submitRequest(<span class="string">"root"</span>, <span class="string">"type:pages"</span>);</span><br><span class="line"></span><br><span class="line">  assertResponseIsXML();</span><br><span class="line">  assertResponseContains(</span><br><span class="line">    <span class="string">"&lt;name&gt;PageOne&lt;/name&gt;"</span>, <span class="string">"&lt;name&gt;PageTwo&lt;/name&gt;"</span>, <span class="string">"&lt;name&gt;ChildOne&lt;/name&gt;"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testSymbolicLinksAreNotInXmlPageHierarchy</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  <span class="type">WikiPage</span> <span class="variable">page</span> <span class="operator">=</span> makePage(<span class="string">"PageOne"</span>);</span><br><span class="line">  makePages(<span class="string">"PageOne.ChildOne"</span>, <span class="string">"PageTwo"</span>);</span><br><span class="line"></span><br><span class="line">  addLinkTo(page, <span class="string">"PageTwo"</span>, <span class="string">"SymPage"</span>);</span><br><span class="line"></span><br><span class="line">  submitRequest(<span class="string">"root"</span>, <span class="string">"type:pages"</span>);</span><br><span class="line"></span><br><span class="line">  assertResponseIsXML();</span><br><span class="line">  assertResponseContains(</span><br><span class="line">    <span class="string">"&lt;name&gt;PageOne&lt;/name&gt;"</span>, <span class="string">"&lt;name&gt;PageTwo&lt;/name&gt;"</span>, <span class="string">"&lt;name&gt;ChildOne&lt;/name&gt;"</span>);</span><br><span class="line">  assertResponseDoesNotContain(<span class="string">"SymPage"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetDataAsXml</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  makePageWithContent(<span class="string">"TestPageOne"</span>, <span class="string">"test page"</span>);</span><br><span class="line"></span><br><span class="line">  submitRequest(<span class="string">"TestPageOne"</span>, <span class="string">"type:data"</span>);</span><br><span class="line"></span><br><span class="line">  assertResponseIsXML();</span><br><span class="line">  assertResponseContains(<span class="string">"test page"</span>, <span class="string">"&lt;Test"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>BUILD-OPERATE-CHECK 패턴이 위와 같은 테스트 구조에 적합</p><ul><li>각 테스트는 명확히 세 부분으로 나눠진다.</li><li>첫 부분은 테스트 자료를 만든다.</li><li>두 번째 부분은 테스트 자료를 조작</li><li>세 번째 부분은 조작한 결과가 올바른지 확인</li></ul><p>테스트 코드는 본론에 돌입해 진짜 필요한 자료유형과 함수만 사용</p><ul><li>잡다하고 세세한 코드를 거의 다 없앰</li><li>읽는 사람으로 하여금 테스트 코드가 수행하는 기능을 재빨리 이해</li></ul><h3 id="도메인-특화된-테스트-언어"><a href="#도메인-특화된-테스트-언어" class="headerlink" title="도메인 특화된 테스트 언어"></a>도메인 특화된 테스트 언어</h3><ul><li>위의 리팩토링된 코드는 DSL로 테스트 코드 구현 기법 보여줌</li><li>흔히 쓰는 시스템 조작 API 사용대신 API 위에 함수, 유틸리티 구현후 사용</li><li>이렇게 구현한 함수와 유틸리티는 테스트 코드에서 사용하는 특수 API가 된다<br>→ 테스트 구현 당사자, 테스트를 읽어볼 독자를 도와주는 테스트 언어</li><li>테스트 API는 처음부터 설계된 API가 아님 → 리팩터링하며 진화된 API</li></ul><h3 id="이중-표준"><a href="#이중-표준" class="headerlink" title="이중 표준"></a>이중 표준</h3><ul><li>테스트 API에 적용하는 표준은 실제 코드에 적용하는 표준과 확실히 다르다</li><li>단순,간결,표현력 풍부는 동일</li><li>실제 코드만큼 효율적일 필요는 없음 → 테스트 환경에서 돌아가는 코드이므로</li></ul><figure class="highlight java"><figcaption><span>EnvironmentControllerTest.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//대충 읽어도 온도가 급격히 떨어지면 경보, 온풍기, 송풍기 확인하는 테스트 코드</span></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOnLoTempAlarmAtThreashold</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  hw.setTemp(WAY_TOO_COLD); </span><br><span class="line">  controller.tic(); </span><br><span class="line">  assertTrue(hw.heaterState());   </span><br><span class="line">  assertTrue(hw.blowerState()); </span><br><span class="line">  assertFalse(hw.coolerState()); </span><br><span class="line">  assertFalse(hw.hiTempAlarm());       </span><br><span class="line">  assertTrue(hw.loTempAlarm());</span><br><span class="line">}</span><br><span class="line"><span class="comment">//상태와 상태값 보느라 가독성 떨어짐</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>EnvironmentControllerTest.java(리팩터링)</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOnLoTempAlarmAtThreshold</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  wayTooCold();</span><br><span class="line">  assertEquals(<span class="string">"HBchL"</span>, hw.getState());</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>설명</p><ul><li><code>wayTooCold</code>함수로 기존 <code>tic</code>함수 숨김</li><li>그릇된 코드 위반에 가깝지만 여기에 적절 → 테스트 코드가 읽기 쉬어짐</li></ul><figure class="highlight java"><figcaption><span>EnvironmentControllerTest.java (bigger selection)</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOnCoolerAndBlowerIfTooHot</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  tooHot();</span><br><span class="line">  assertEquals(<span class="string">"hBChl"</span>, hw.getState()); </span><br><span class="line">}</span><br><span class="line">  </span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOnHeaterAndBlowerIfTooCold</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  tooCold();</span><br><span class="line">  assertEquals(<span class="string">"HBchl"</span>, hw.getState()); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOnHiTempAlarmAtThreshold</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  wayTooHot();</span><br><span class="line">  assertEquals(<span class="string">"hBCHl"</span>, hw.getState()); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Test</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOnLoTempAlarmAtThreshold</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">  wayTooCold();</span><br><span class="line">  assertEquals(<span class="string">"HBchL"</span>, hw.getState()); </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>테스트 코드 이해가 너무 쉬어짐</li></ul><figure class="highlight java"><figcaption><span>MockControlHardware.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">getState</span><span class="params">()</span> {</span><br><span class="line">  <span class="type">String</span> <span class="variable">state</span> <span class="operator">=</span> <span class="string">""</span>;</span><br><span class="line">  state += heater ? <span class="string">"H"</span> : <span class="string">"h"</span>; </span><br><span class="line">  state += blower ? <span class="string">"B"</span> : <span class="string">"b"</span>; </span><br><span class="line">  state += cooler ? <span class="string">"C"</span> : <span class="string">"c"</span>; </span><br><span class="line">  state += hiTempAlarm ? <span class="string">"H"</span> : <span class="string">"h"</span>; </span><br><span class="line">  state += loTempAlarm ? <span class="string">"L"</span> : <span class="string">"l"</span>; </span><br><span class="line">  <span class="keyword">return</span> state;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>성능적으로 효율적이지 못한 코드</li><li>효율을 높이려면 <code>StringBuffer</code>가 더 작합하지만 보기에 흉하다</li><li>위 코드는 <code>StringBuffer</code>를 사용하지 않아 치루는 비용이 미미→ 테스트에선 자원 제한일 가능성이 낮으므로</li></ul><p>이중 표준의 본질</p><ul><li>실제 환경에서는 절대 안되지만 테스트 환경에서는 전혀 문제 없는 방식이 존재</li><li>대게 메모리,cpu효율 관련</li><li>클린 코드 여부와는 철저히 무관</li></ul><h2 id="테스트당-assert-하나"><a href="#테스트당-assert-하나" class="headerlink" title="테스트당 assert 하나"></a>테스트당 assert 하나</h2><p>테스트 코드시 함수마다 assert 하나만 사용?</p><ul><li>그래야 한다는 파 존재</li><li>확실한 장점이 있음 → 결론이 하나라 코드가 이해하기 쉽고 빠름</li><li>모두 적용되지 않음</li><li>위 9-2_리팩터링 코드는</li><li>“출력이 XML”이다라는 assert와</li><li>“특정 문자열을 포함”이라는 assert가 있으며</li><li>이 둘을 하나로 병합하는 방식이 불합리해보임</li><li>방법 : 테스트를 쪼개 각자가 assert 수행</li></ul><figure class="highlight java"><figcaption><span>SerializedPageResponderTest.java (단일 Assert)</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetPageHierarchyAsXml</span><span class="params">()</span> <span class="keyword">throws</span> Exception { </span><br><span class="line">  givenPages(<span class="string">"PageOne"</span>, <span class="string">"PageOne.ChildOne"</span>, <span class="string">"PageTwo"</span>);</span><br><span class="line">  </span><br><span class="line">  whenRequestIsIssued(<span class="string">"root"</span>, <span class="string">"type:pages"</span>);</span><br><span class="line">  </span><br><span class="line">  thenResponseShouldBeXML(); </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testGetPageHierarchyHasRightTags</span><span class="params">()</span> <span class="keyword">throws</span> Exception { </span><br><span class="line">  givenPages(<span class="string">"PageOne"</span>, <span class="string">"PageOne.ChildOne"</span>, <span class="string">"PageTwo"</span>);</span><br><span class="line">  </span><br><span class="line">  whenRequestIsIssued(<span class="string">"root"</span>, <span class="string">"type:pages"</span>);</span><br><span class="line">  </span><br><span class="line">  thenResponseShouldContain(</span><br><span class="line">    <span class="string">"&lt;name&gt;PageOne&lt;/name&gt;"</span>, <span class="string">"&lt;name&gt;PageTwo&lt;/name&gt;"</span>, <span class="string">"&lt;name&gt;ChildOne&lt;/name&gt;"</span></span><br><span class="line">  ); </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>given-when-then 관례 사용<ul><li>테스트 코드 일기가 쉬워짐</li><li>불행히도 위에서 보듯이 테스트를 분리하면 중복된 코드가 많아짐</li><li>해결? 템플릿 메서드 패턴 사용시 중복 제거 가능</li></ul></li><li>템플릿 메서드 패턴 : 중복 제거 가능<ul><li>given/when을 부모 클래스에</li><li>then을 자식 클래스에</li></ul></li><li>다른 방법?<ul><li>독자적 클래스를 만들고 <code>@Before</code>에 given when을 넣고 <code>@Test</code>에 then부분을 넣어도 된다</li></ul></li><li>문제는? <strong>모두가 배보다 배꼽이 크다</strong></li><li>모든걸 감안하면 결국 9-2처럼 assert을 여러 개를 사용하는 것이 좋다</li></ul><h2 id="테스트당-개념-하나"><a href="#테스트당-개념-하나" class="headerlink" title="테스트당 개념 하나"></a>테스트당 개념 하나</h2><p><strong>→테스트 함수마다 한 개념만 테스트하라</strong></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * addMonth() 메서드를 테스트하는 장황한 코드</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testAddMonths</span><span class="params">()</span> {</span><br><span class="line">  <span class="type">SerialDate</span> <span class="variable">d1</span> <span class="operator">=</span> SerialDate.createInstance(<span class="number">31</span>, <span class="number">5</span>, <span class="number">2004</span>);</span><br><span class="line"></span><br><span class="line">  <span class="type">SerialDate</span> <span class="variable">d2</span> <span class="operator">=</span> SerialDate.addMonths(<span class="number">1</span>, d1); </span><br><span class="line">  assertEquals(<span class="number">30</span>, d2.getDayOfMonth()); </span><br><span class="line">  assertEquals(<span class="number">6</span>, d2.getMonth()); </span><br><span class="line">  assertEquals(<span class="number">2004</span>, d2.getYYYY());</span><br><span class="line">  </span><br><span class="line">  <span class="type">SerialDate</span> <span class="variable">d3</span> <span class="operator">=</span> SerialDate.addMonths(<span class="number">2</span>, d1); </span><br><span class="line">  assertEquals(<span class="number">31</span>, d3.getDayOfMonth()); </span><br><span class="line">  assertEquals(<span class="number">7</span>, d3.getMonth()); </span><br><span class="line">  assertEquals(<span class="number">2004</span>, d3.getYYYY());</span><br><span class="line">  </span><br><span class="line">  <span class="type">SerialDate</span> <span class="variable">d4</span> <span class="operator">=</span> SerialDate.addMonths(<span class="number">1</span>, SerialDate.addMonths(<span class="number">1</span>, d1)); </span><br><span class="line">  assertEquals(<span class="number">30</span>, d4.getDayOfMonth());</span><br><span class="line">  assertEquals(<span class="number">7</span>, d4.getMonth());</span><br><span class="line">  assertEquals(<span class="number">2004</span>, d4.getYYYY());</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>바람직하지 못한 코드</p><ul><li>독자적인 개념 3개를 테스트 → 독자적 테스트 3개로 쪼개야</li><li>한 함수일경우 <strong>독자가 각 절이 거기에 존재하는 이유와 각 절이 테스트하는 개념을 모두 이해해야 한다.</strong></li></ul><p>분리한 테스트 코드</p><ul><li>(5월처럼) 31일로 끝나는 달의 마지막 날짜가 주어지는 경우</li></ul><ol><li>(6월처럼) 30일로 끝나는 한 달을 더하면 날짜는 30일이 되어야지 31일이 되어서는 안 된다.</li><li>두 달을 더하면 그리고 두 번째 달이 31일로 끝나면 날짜는 31일이 되어야 한다.</li></ol><ul><li>(6월처럼) 30일로 끝나는 달의 마지막 날짜가 주어지는 경우<ol><li>31일로 끝나는 한 달을 더하면 날짜는 30일이 되어야지 31일이 되면 안 된다.</li></ol></li></ul><p>분리하면? → 감춰진 일반적인 규칙이 보임</p><ul><li>날짜에 어떤 달을 더 하면 날짜는 그달의 마지막 날짜보다 커지지 못함<br>→ 2월+28일 + 1달 = 3월 28일 → 채우면 좋을 테스트 케이스</li></ul><p>포인트</p><ul><li>개념당 assert 문을 최소로</li><li>세트스 함수당 개념 하나만 테스트</li></ul><h2 id="F-I-R-S-T"><a href="#F-I-R-S-T" class="headerlink" title="F.I.R.S.T"></a>F.I.R.S.T</h2><ul><li>이미지 from <img src="/dev-book/cleancode/CleanCode-ch9/FIRST.jpg" class="" title="F.I.R.S.T http://agileinaflash.blogspot.com/2009/02/first.html"></li></ul><h3 id="1-FAST"><a href="#1-FAST" class="headerlink" title="1. FAST"></a>1. FAST</h3><ul><li>테스트는 빨라야 한다</li><li>느리면 자주 못 돌림 → 초반에 문제 못 찾음, 코드 마음껏 정리 불가 → 코드 품질 망가짐</li></ul><h3 id="2-Isolates"><a href="#2-Isolates" class="headerlink" title="2. Isolates"></a>2. Isolates</h3><ul><li>각 테스트는 서로 의존하면 안된다</li><li>각 테스트는 독립적으로 어떤 순서로 실행해도 괜찮아야 한다</li><li>테스트가 의존하면? → 연쇄 실패 발생 → 원인 진단 어려움 → 테스트 결함 숨겨짐</li></ul><h3 id="3-Repeatable"><a href="#3-Repeatable" class="headerlink" title="3. Repeatable"></a>3. Repeatable</h3><ul><li>테스트는 어떤 환경에서도 반복 가능해야 한다</li><li>실제 환경, QA 환경, 오프라인된 노트북에서도 실행 가능해야 한다</li><li>테스트가 돌아가지 않는 환경이 하나라도 있으면 안된다</li></ul><h3 id="4-Self-validating"><a href="#4-Self-validating" class="headerlink" title="4. Self-validating"></a>4. Self-validating</h3><ul><li>테스트는 bool값으로 결과를 내야 한다</li><li>스스로 성공과 실패가 가늠해야 한다</li><li>그러지 않으면? → 주관적 판단 → 지루한 수작업 평가</li></ul><h3 id="4-Timely"><a href="#4-Timely" class="headerlink" title="4. Timely"></a>4. Timely</h3><ul><li>테스트는 적시에 작성해야 한다</li><li>테스트 하려는 실제 코드 구현 직전에 구현한다</li><li>실제 코드 구현 후엔? 실제 코드가 테스트 하기 어렵다는 사실 발견</li><li>테스트가 불가능한 실제 코드 설계 했을 수도</li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li>‘깨끗한 테스트 코드’는 책 한 권을 할애해도 모자랄 주제다</li><li>테스트 코드는 실제 코드의 유연성, 유지보수성, 재사용성을 보존/강화<br>→실제 구현 코드보다 더 중요할지도</li><li>테스트 코드는<ul><li>깨끗하게 지속적으로 관리</li><li>표현력 높이고 간결하게 정리</li><li>테스트 API를 구현해 DSL을 만들어 테스트 코드를 짜기 쉽게 만들자</li></ul></li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch9/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅷ. 경계</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch8/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch8/</guid>
      <pubDate>Wed, 09 Feb 2022 00:53:46 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅷ-경계&quot;&gt;&lt;a href=&quot;#Ⅷ-경계&quot; class=&quot;headerlink&quot; title=&quot;Ⅷ. 경계&quot;&gt;&lt;/a&gt;Ⅷ. 경계&lt;/h1&gt;&lt;p</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅷ-경계"><a href="#Ⅷ-경계" class="headerlink" title="Ⅷ. 경계"></a>Ⅷ. 경계</h1><p>모든 프로그램을 직접 개발하지 않음</p><ul><li>패키지, 오픈소스, 다른 사내의 컴포넌트등</li><li>외부 코드와 우리 코드를 깔끔하게 통합하는 방법?</li></ul><h2 id="외부-코드-사용"><a href="#외부-코드-사용" class="headerlink" title="외부 코드 사용"></a>외부 코드 사용</h2><p>인터페이스 제공자와 인터페이스 사용자의 긴장</p><ul><li>제공자<ul><li>패키지 제공자, 프레임워크 제공자</li><li>적용성 최대한 넓히려 → 더 많은 환경에서 돌아가도록</li></ul></li><li>사용자<br>– 자신의 요구에 집중하는 인터페이스를 원함</li></ul><p>Java.util.Map</p><ul><li>Map : 굉장히 다양한 인터페이스로 수많은 기능 제공<img src="/dev-book/cleancode/CleanCode-ch8/map-interface-structure.png" class="" title="Java.util.Map 구조"></li><li>Map의 기능성/유연성은 유용하나 위험도 큼</li><li>Map을 여기저기 넘길때 Map.clear()로 누구나 내용 삭제 가능</li><li>객체 유형 제한 없음 : 마음만 먹으면 어떤 객체든 추가 가능<figure class="highlight java"><figcaption><span>일반적인 Map 사용</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Sensor 객체를 담는 Map</span></span><br><span class="line"><span class="type">Map</span> <span class="variable">sensors</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>(); </span><br><span class="line"><span class="comment">// Sensor 가 필요하면</span></span><br><span class="line"><span class="type">Sensor</span> <span class="variable">s</span> <span class="operator">=</span> (Sensor) sensors.get(sensorId); </span><br></pre></td></tr></tbody></table></figure></li><li>동작은 하나 클린 코드는 아님<ul><li>Map의 리턴값인 Object의 cast 책임이 클라이언트에 있음</li><li>코드의 의도가 들어나지 않음</li></ul></li></ul><figure class="highlight java"><figcaption><span>Generics를 사용해서 가독성을 올린 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;Sensor&gt; sensors = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;Sensor&gt;();</span><br><span class="line"><span class="type">Sensor</span> <span class="variable">s</span> <span class="operator">=</span> sensors.get(sensorId);</span><br></pre></td></tr></tbody></table></figure><ul><li>사용자에게 필요하지 않은 기능까지 제공하는 문제를 해결하지 못함</li><li>Map 인터페이스도 변함 (ex)자바5의 제네릭 추가등 : 이경우 수정코드가 많음</li></ul><figure class="highlight java"><figcaption><span>Map을 좀 더 깔끔하게 사용한 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sensors</span> {</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Map</span> <span class="variable">sensors</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HashMap</span>(); </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> Sensor <span class="title function_">getById</span><span class="params">(String id)</span> {</span><br><span class="line">        <span class="keyword">return</span> (Sensor)sensors.get(id);</span><br><span class="line">    }</span><br><span class="line">    <span class="comment">//이하 생략</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>경계 인터페이스인 Map을 Sensors 안으로 숨김<ul><li>Map 인터페이스가 변하더라도 나머지 프로그램에 영향 없음</li><li>제네릭 사용 여부에 영향 X → Sensors 클래스 안에서 객체 유형 관리/변환</li></ul></li><li>Sensors 클래스가 프로그램에 필요한 인터페이스만 제공<ul><li>코드 이해가 쉬우며 오용하기는 어려움</li><li>나머지 사용 클라이언트에 설계 규칙/ 비지니스 규칙을 따르도록 강제 가능</li></ul></li></ul><p><strong>Map(또는 유사한 경계 인터페이스를 )여기저기 넘기지 마라</strong></p><ul><li>이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의</li><li>공개 API 인자나 반환값으로 Map 인스턴스를 사용하면 안된다</li></ul><h2 id="경계-살피고-익히기"><a href="#경계-살피고-익히기" class="headerlink" title="경계 살피고 익히기"></a>경계 살피고 익히기</h2><ul><li>외부 패키지 테스트는 우리 책임이 아니나 사용할 코드를 테스트 함이 바람직함</li><li>외부 코드 익히기 어려움<ul><li>타사 라이브러리 문서 읽음으로사용법 결정</li><li>코드 작성해서 라이브러리 예쌍 작동 확인</li><li>디버깅 문제 : 우리 코드 버그 or 라이브러리 버그?<br>학습 테스트</li></ul></li><li>간단한 테스트 케이스를 작성해서 외부 코드를 익히는 방법</li><li>프로그램에서 사용하는 방식대로 외부 API 호출</li><li>통제된 환경에서 API를 제대로 이해하는가 확인</li><li>API 사용목적 초점</li></ul><h2 id="log4j-익히기"><a href="#log4j-익히기" class="headerlink" title="log4j 익히기"></a>log4j 익히기</h2><ul><li><p>학습 테스트</p><img src="/dev-book/cleancode/CleanCode-ch8/test-1.png" class="" title="log4j test-1"><img src="/dev-book/cleancode/CleanCode-ch8/test-2.png" class="" title="log4j test-2"><img src="/dev-book/cleancode/CleanCode-ch8/test-3.png" class="" title="log4j test-3"><img src="/dev-book/cleancode/CleanCode-ch8/test-4.png" class="" title="log4j test-4"></li><li><p>지금까지 간단한 콘솔 로거 초기화 방법 익힘</p></li><li><p>이제 모든 지식을 독자적 로거 클래스로 캡슐화</p></li><li><p>나머지 프로그램은 log4j 경계 인터페이스를 몰라도 사용 가능</p></li></ul><h2 id="학습-테스트는-공짜-이상이다"><a href="#학습-테스트는-공짜-이상이다" class="headerlink" title="학습 테스트는 공짜 이상이다"></a>학습 테스트는 공짜 이상이다</h2><ul><li>학습테스트는 이해도를 높여주는 정확한 실험</li><li>투자노력보다 얻는 성과가 더 큼</li><li>패키지 새 버전 나오면 학습 테스트를 돌려서 차이가 있는지 검증 가능</li><li>이런 경계 테스트가 있으면 패키지 새 버전 이전이 쉬어짐</li><li>아니면 필요이상 낡은 버전을 오랫동안 사용하려는 유혹에 빠지기 쉬움</li></ul><h2 id="아직-존재하지-않는-코드를-사용하기"><a href="#아직-존재하지-않는-코드를-사용하기" class="headerlink" title="아직 존재하지 않는 코드를 사용하기"></a>아직 존재하지 않는 코드를 사용하기</h2><p>경게의 또 다른 유형 : 아는코드와 모르는 코드를 분리하는 경계<br>예시)</p><ul><li>상황<ul><li>sw에 ‘송신기’가 필요</li><li>송신기 시스템 책임진 사람들은 인터페이스도 정의하지 못한 상태</li><li>프로젝트 지연 없게 하기 위해 송신기와 먼 부분부터 작업</li><li>작업 부분과 송신기 시스템 부분의 경계를 부딫치게 되며 기능정의를 하게 되었음<ul><li><blockquote><p>지정한 주파수를 이용해 이 스트림에서 들어오는 자료를 아날로그 신호로 전송하라</p></blockquote></li></ul></li><li>아직 API설계가 되지 않으므로 구체적인 방법은 모름 → 구현을 나중으로 </li><li>자체적 인터페이스 정의<ul><li>Transmitter 인터페이스 클래스 정의</li><li>transmit 메서드 추가</li><li>주파수와 자료스트림을 입력받음</li></ul></li></ul></li><li>바라는 인터페이스 구현 → 인터페이스 전적 통제한다는 장점 → 코드 가독성과 분명한 코드 의도 반영<img src="/dev-book/cleancode/CleanCode-ch8/transmitter-predictable.png" class="" title="송신기 예측"></li><li>(통제할수도 없고 정의되지도 않은) 송신기 API에서 Communication Controller 분리<ul><li>필요한 인터페이스 정의 했으므로 Communication Controller는 깔끔 / 깨끗</li><li>실제 송신기 API가 정의된 뒤에는 Transmitter Adapter 구현으로 간극을 매운다</li><li>ADAPTER 패턴 사용 → API 사용 캡슐화 → API 수정 시 수정될 코드를 한 곳으로</li><li>테스트도 간편 : 적절한 Fake Transmitter 사용 → Communication Controller 테스트 가능</li></ul></li><li>Transmitter API 인터페이스가 나온다음에 경계 테스트 생성해서 올바로 API 사용하는지도 테스트 가능</li></ul><h2 id="깨끗한-경계"><a href="#깨끗한-경계" class="headerlink" title="깨끗한 경계"></a>깨끗한 경계</h2><p>경계의 변경</p><ul><li>sw 개발설계가 우수하다면? 변경시 많은 투자와 재작업이 필요하지 않음</li><li>통제 못하는 코드 사용시 너무 많은 투자나 향후 변경 비용이 너무 커지지 않도록 각별히 유의</li></ul><p>경계 코드는 깔끔히 분리</p><ul><li>기대치 정의 테스트 ㅂ작성</li><li>외부 호출한 코드를 가능한 줄여 경계를 관리하자<ul><li>Map의 예처럼 새로운 클래스로 경계를 감싸거나</li><li>ADAPTER 패턴을 사용해 우리가 원하는 인터페이스를 패키지가 제공하는 인터페이스로 변환하자</li><li>어떤 방법이든 가독성향상, 경계 인터페이스 사용 일관성 향상, 패키지 변경시 변경 코드 감소</li></ul></li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch8/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅶ. 오류 처리</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch7/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch7/</guid>
      <pubDate>Wed, 09 Feb 2022 00:53:26 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅶ-오류-처리&quot;&gt;&lt;a href=&quot;#Ⅶ-오류-처리&quot; class=&quot;headerlink&quot; title=&quot;Ⅶ. 오류 처리&quot;&gt;&lt;/a&gt;Ⅶ. </description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅶ-오류-처리"><a href="#Ⅶ-오류-처리" class="headerlink" title="Ⅶ. 오류 처리"></a>Ⅶ. 오류 처리</h1><p>클린 코드와 오류처리의 상관관계</p><ul><li>오류의 가능성은 늘 언제나 존재</li><li>오류 정정의 책임은 프로그래머가 가지고 있음</li><li>여기저기 흩어진 오류 처리 코드 → 실제 코드 로직 파악 어렵게 만듬</li></ul><h2 id="오류-코드-보다-예외-사용"><a href="#오류-코드-보다-예외-사용" class="headerlink" title="오류 코드 보다 예외 사용"></a>오류 코드 보다 예외 사용</h2><p>이전 프로그래밍 언어 → Exception 제공 x</p><ul><li>개발자들이 에러 상태나 flag를 set해야 함</li><li>에러코드를 리턴, 호출하는 측에서 예외 처리해줘야 함<figure class="highlight java"><figcaption><span>안좋은 예  오류 플래그, 호출자에게 오류 코드 반환 등</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DeviceController</span> {</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendShutDown</span><span class="params">()</span> {</span><br><span class="line">    <span class="type">DeviceHandle</span> <span class="variable">handle</span> <span class="operator">=</span> getHandle(DEV1);</span><br><span class="line">    <span class="comment">// Check the state of the device</span></span><br><span class="line">    <span class="keyword">if</span> (handle != DeviceHandle.INVALID) {</span><br><span class="line">      <span class="comment">// Save the device status to the record field</span></span><br><span class="line">      retrieveDeviceRecord(handle);</span><br><span class="line">      <span class="comment">// If not suspended, shut down</span></span><br><span class="line">      <span class="keyword">if</span> (record.getStatus() != DEVICE_SUSPENDED) {</span><br><span class="line">        pauseDevice(handle);</span><br><span class="line">        clearDeviceWorkQueue(handle);</span><br><span class="line">        closeDevice(handle);</span><br><span class="line">      } <span class="keyword">else</span> {</span><br><span class="line">        logger.log(<span class="string">"Device suspended. Unable to shut down"</span>);</span><br><span class="line">      }</span><br><span class="line">    } <span class="keyword">else</span> {</span><br><span class="line">      logger.log(<span class="string">"Invalid handle for: "</span> + DEV1.toString());</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">  ...</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>위 코드 단점<ul><li>호출자 코드 복잡 : 호출 리턴 받은 즉시 오류 확인 필요</li><li>불행히 이 단계를 잊어버리기 쉬움</li></ul></li><li>Exception 사용하면?<ul><li>깔끔해짐</li><li>겉보기만 아름다워지는 것이 아님</li><li>실제 로직과 예외처리 부분이 나누어짐</li><li>필요 부분에 집중<figure class="highlight java"><figcaption><span>오류 발견시 예외를 던지는 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DeviceController</span> {</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sendShutDown</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      tryToShutDown();</span><br><span class="line">    } <span class="keyword">catch</span> (DeviceShutDownError e) {</span><br><span class="line">      logger.log(e);</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">tryToShutDown</span><span class="params">()</span> <span class="keyword">throws</span> DeviceShutDownError {</span><br><span class="line">    <span class="type">DeviceHandle</span> <span class="variable">handle</span> <span class="operator">=</span> getHandle(DEV1);</span><br><span class="line">    <span class="type">DeviceRecord</span> <span class="variable">record</span> <span class="operator">=</span> retrieveDeviceRecord(handle);</span><br><span class="line">    pauseDevice(handle); </span><br><span class="line">    clearDeviceWorkQueue(handle); </span><br><span class="line">    closeDevice(handle);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> DeviceHandle <span class="title function_">getHandle</span><span class="params">(DeviceID id)</span> {</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">DeviceShutDownError</span>(<span class="string">"Invalid handle for: "</span> + id.toString());</span><br><span class="line">    ...</span><br><span class="line">  }</span><br><span class="line">  ...</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul></li><li>개념 분리 : 디바이스 종료 알고리즘 + 오류 처리 알고리즘</li><li>각 개념을 독립적으로 살펴보기 가능해짐</li></ul><h2 id="try-catch-Finally부터-작성하라"><a href="#try-catch-Finally부터-작성하라" class="headerlink" title="try-catch-Finally부터 작성하라"></a>try-catch-Finally부터 작성하라</h2><ul><li>try블록은 트랜잭션과 비슷</li><li>try가 어떻든 catch에서는 프로그램 상태를 일관성 있게 유지</li><li>코드의 scope 정의 가능<br>예시<figure class="highlight java"><figcaption><span>TEST 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//예외를 기대하는 단위 테스트 코드</span></span><br><span class="line"><span class="meta">@Test(expected = StorageException.class)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">retrieveSectionShouldThrowOnInvalidFileName</span><span class="params">()</span> {</span><br><span class="line">  sectionStore.retrieveSection(<span class="string">"invalid - file"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><figure class="highlight java"><figcaption><span>1. 구현코드1 : 예외를 던지지 않으므로 실패</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> List&lt;RecordedGrip&gt; <span class="title function_">retrieveSection</span><span class="params">(String sectionName)</span> {</span><br><span class="line">  <span class="comment">// dummy return until we have a real implementation</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;RecordedGrip&gt;();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>예외를 던지는 구현 코드로 테스트 성공</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">  <span class="keyword">public</span> List&lt;RecordedGrip&gt; <span class="title function_">retrieveSection</span><span class="params">(String sectionName)</span> {</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      <span class="type">FileInputStream</span> <span class="variable">stream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(sectionName)</span><br><span class="line">    } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StorageException</span>(<span class="string">"retrieval error"</span>, e);</span><br><span class="line">    }</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;RecordedGrip&gt;();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>이제부터 리팩토링 가능</li></ul><figure class="highlight java"><figcaption><span>catch 블럭에서 예외 유형 좁히기</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Exception의 범위를 FileNotFoundException으로 줄여 잡아낸다.</span></span><br><span class="line"><span class="keyword">public</span> List&lt;RecordedGrip&gt; <span class="title function_">retrieveSection</span><span class="params">(String sectionName)</span> {</span><br><span class="line">  <span class="keyword">try</span> {</span><br><span class="line">    <span class="type">FileInputStream</span> <span class="variable">stream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(sectionName);</span><br><span class="line">    stream.close();</span><br><span class="line">  } <span class="keyword">catch</span> (FileNotFoundException e) {</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StorageException</span>(<span class="string">"retrieval error"</span>, e);</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;RecordedGrip&gt;();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>try catch구조로 범위 정의 → TDD를 사용해 나머지 논리 추가</li><li>나머지 논리는 <code>FileInputStream</code>생성과 close()사이에 넣는다</li><li><strong>먼저 강제로 예외를 일으키는 테스트 케이스를 작성 후 테스트를 통과하게 코드를 작성하는 방법 권장</strong></li><li>자연스럽게 try블록의 트랜잭션 부터 구현하게됨 → 범위 내에서 트랜잭션 본일 유지가 쉬워짐</li></ul><h2 id="unchecked-예외를-사용하라"><a href="#unchecked-예외를-사용하라" class="headerlink" title="unchecked 예외를 사용하라"></a>unchecked 예외를 사용하라</h2><p>논쟁은 끝났다 : unchecked 사용하라!</p><p>checked Execption vs Unchecked Exception</p><ul><li>가장 명확한 기준 : 꼭 처리를 해야 하는 부분</li><li>확인된 예외 : 반드시 try/catch로 감싸거나 throw로 처리해야 한다</li></ul><p>이전엔는 확인된 예외를 좋은 아이디어로 생각</p><ul><li>확인된 예외 : checked exception</li><li>북구될 가능성이 있는 문제상황으로 봄</li><li>컴파일단에서 확인이 가능하고 복구를 시도해 보는 것이 일차적 관심상</li><li>메서드 선언시 메서드가 반환할 예외 모두 열거</li><li>메서드 반환 예외는 메서드 유형의 일부</li><li>코드가 메서드를 사용하는 방식이 메서드 선언과 일치하지 않으면 아예 컴파일도 불가</li></ul><p>현재는 확인된 예외가 반드시 필요하지 않는다</p><ul><li>c#, c++, 파이썬, 루비 : 확인된 예외가 없지만 안정적인 sw만드는데 문리가 없음</li></ul><p>확인된 예외(checked exception)의 비용</p><ol><li>위에서 말했듯이 모든 중간 단계 메소드에 해당 exception을 throw 해야 함</li><li>OCP 위반</li></ol><ul><li>메서드에서 확인된 예외를 던졌는데 catch 블록이 3단계 위라면</li><li>그 사이 메서드 모두 선언부에 해당 예외를 정의해야함 (throw 절)</li><li>하위 단계 코드를 변경하면 상위 단계 메서드 선언부를 전부 고쳐야함(새로운 예외 추가or throw절 추가등)</li><li>(코드가 전혀 변경이 없어도) 선언부가 바뀌므로 모듈 다시 빌드하고 배포해야함</li><li>throws 경로의 모든 함수가 최하위 함수의 예외(하위 세부)를 알아야 함 → 캡슐화가 꺠진다</li><li>오류를 원거리에서 처리하기 위해 예외를 사용했는데 예외가 캡슐화를 깨버리는 아이러니</li></ul><p>때로는 유용</p><ul><li>아주 중요한 라이브러리라면 모든 예외를 잡아야함</li><li>일반적 어플리케이션? 의존성 비용&gt;&gt;&gt;&gt;&gt;&gt; 예외로 얻는 이득</li></ul><h2 id="예외에-메시지를-담아라-예외에-의미있는-정보를-담기"><a href="#예외에-메시지를-담아라-예외에-의미있는-정보를-담기" class="headerlink" title="예외에 메시지를 담아라 - 예외에 의미있는 정보를 담기"></a>예외에 메시지를 담아라 - 예외에 의미있는 정보를 담기</h2><p>예외를 던질때 전주 상황을 충분히 덧붙여라</p><ul><li>오류가 발생한 원인/위치 찾기가 쉬어짐</li><li>자바는 모든 예외에 호출 스택을 제공 → 실패 코드 의도 파악을 위해선 해당 정보로 부족</li><li>오류 메시지에 정보를 담아 예외와 함께 던져야함<ul><li>실패한 연산 이름, 실패 유형도 언급</li><li>로깅 사용시 catch 블럭에서 오류룰 기록하도록 충분한 정보를 넘겨준다</li></ul></li></ul><h2 id="호출자를-고려해서-예외-클래스-정의"><a href="#호출자를-고려해서-예외-클래스-정의" class="headerlink" title="호출자를 고려해서 예외 클래스 정의"></a>호출자를 고려해서 예외 클래스 정의</h2><ul><li>오류 분류방법은 수없이 많음 : 오류 발생한 위치, 유형등</li><li>프로그래머 가장 큰 관심사 : <strong>오류를 잡아내는 방법</strong><figure class="highlight java"><figcaption><span>형편 없는 오류 분류 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 외부 라이브러리가 던질 예외를 모두 잡음</span></span><br><span class="line"><span class="comment">// 중복도 심함</span></span><br><span class="line"><span class="type">ACMEPort</span> <span class="variable">port</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ACMEPort</span>(<span class="number">12</span>);</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line">  port.open();</span><br><span class="line">} <span class="keyword">catch</span> (DeviceResponseException e) {</span><br><span class="line">  reportPortError(e);</span><br><span class="line">  logger.log(<span class="string">"Device response exception"</span>, e);</span><br><span class="line">} <span class="keyword">catch</span> (ATM1212UnlockedException e) {</span><br><span class="line">  reportPortError(e);</span><br><span class="line">  logger.log(<span class="string">"Unlock exception"</span>, e);</span><br><span class="line">} <span class="keyword">catch</span> (GMXError e) {</span><br><span class="line">  reportPortError(e);</span><br><span class="line">  logger.log(<span class="string">"Device response exception"</span>);</span><br><span class="line">} <span class="keyword">finally</span> {</span><br><span class="line">  ...</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>일반적인 상황에서 우리가 오류를 처리하는 방식<br>→ (오류 원인과 무관하게) 비교적 일정</li></ul><ol><li>오류를 기록한다</li><li>프로그램을 계속 수행해도 좋은지 확인한다</li></ol><p>위의 코드 의 경우<br>→ 예외 대응 방식이 예외 유형과 무관하게 거의 동일</p><ul><li>호출하는 라이브러리 api를 감싸면서 예외 유형 하나를 반환하도록</li></ul><figure class="highlight java"><figcaption><span>LocalPort → ACMEPort Wrapper class</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 호출부 - Exception 처리가 들어나지 않도록 수행 가능</span></span><br><span class="line"><span class="type">LocalPort</span> <span class="variable">port</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LocalPort</span>(<span class="number">12</span>);</span><br><span class="line">  <span class="keyword">try</span> {</span><br><span class="line">    port.open();</span><br><span class="line">  } <span class="keyword">catch</span> (PortDeviceFailure e) {</span><br><span class="line">    reportError(e);</span><br><span class="line">    logger.log(e.getMessage(), e);</span><br><span class="line">  } <span class="keyword">finally</span> {</span><br><span class="line">    ...</span><br><span class="line">  }</span><br><span class="line"><span class="comment">//---------------------------------------------  </span></span><br><span class="line"><span class="comment">//ACMEPort가 던지는 예외를 잡아 변환하는 wrapper class</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LocalPort</span> {</span><br><span class="line">    <span class="keyword">private</span> ACMEPort innerPort;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LocalPort</span><span class="params">(<span class="type">int</span> portNumber)</span> {</span><br><span class="line">      innerPort = <span class="keyword">new</span> <span class="title class_">ACMEPort</span>(portNumber);</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">open</span><span class="params">()</span> {</span><br><span class="line">      <span class="keyword">try</span> {</span><br><span class="line">        innerPort.open();</span><br><span class="line">      } <span class="keyword">catch</span> (DeviceResponseException e) {</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">PortDeviceFailure</span>(e);</span><br><span class="line">      } <span class="keyword">catch</span> (ATM1212UnlockedException e) {</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">PortDeviceFailure</span>(e);</span><br><span class="line">      } <span class="keyword">catch</span> (GMXError e) {</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">PortDeviceFailure</span>(e);</span><br><span class="line">      }</span><br><span class="line">    }</span><br><span class="line">    ...</span><br><span class="line">  }</span><br></pre></td></tr></tbody></table></figure><p>wrapping의 유용성</p><ul><li>실제로 외부 api 사용시 wrapping 기법이 최선</li><li>외부 api wrapping → 외부 라이브러리와 프로그램 사이의 의존성이 크게 줄어든다</li><li>나중에 다른 라이브러리로 갈아타도 비용이 적음</li><li>wrapping 클래스에서 외부 api 호출 대신 테스트 코드 넣어주는 방법으로 테스트도 쉬어짐</li><li>wrapping 사용하면 특정 업체가 api를 설계한 방식에 발목 잡히지 않음</li><li>위의 예제에선 port 디바이스 실패를 표현하는 예외 유형 하나 정의 → 프로그램이 깨끗해짐</li></ul><p>예외 클래스 수</p><ul><li>예외 클래스가 하나만 있어도 충분한 코드 → 예외 클래스에 포함된 정보로 오류를 구분해도 괜찮은 경우</li><li>한 예외 잡아내고 다른 예외 무시해도 괜찮다면? 여러 예외 클래스 사용</li></ul><h2 id="정상-흐름을-정의하라"><a href="#정상-흐름을-정의하라" class="headerlink" title="정상 흐름을 정의하라"></a>정상 흐름을 정의하라</h2><ul><li>앞절의 지침 충실히 → 비지니스와 오류처리가 잘 분리된 코드</li><li>오류 감지가 프로그램 언저리로 밀려나게 됨<ul><li>외부 API를 감싸 독자적 예외 던짐</li><li>코드위에 처리기를 정의해 중단된 계산을 처리</li><li>대부분은 좋은 처리 방식이지만 이런 중단이 적합하지 않을 때도 있음</li></ul></li></ul><figure class="highlight java"><figcaption><span>총계를 계산하는 허술한 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line">  <span class="type">MealExpenses</span> <span class="variable">expenses</span> <span class="operator">=</span> expenseReportDAO.getMeals(employee.getID());</span><br><span class="line">  m_total += expenses.getTotal(); <span class="comment">//식비 비용 청구 → 청구 식비를 총계에 더함</span></span><br><span class="line">} <span class="keyword">catch</span>(MealExpensesNotFound e) {</span><br><span class="line">  m_total += getMealPerDiem(); <span class="comment">//식비 비용 청구 안하면 → 일일 기본 식비를 총계에 더함</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 예외가 논리를 따라가기 어렵게 만듬</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>특수상황을 처리할 필요가 없으면 코드가 더 간결해진다.. 다음처럼 가능한가?</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">MealExpenses</span> <span class="variable">expenses</span> <span class="operator">=</span> expenseReportDAO.getMeals(employee.getID());</span><br><span class="line">  m_total += expenses.getTotal();</span><br></pre></td></tr></tbody></table></figure><p>정답 : 가능함</p><ul><li>ExpenseReportDAO가 언제나 MealExpense 객체 반환하도록 고침</li><li>청구 식비가 없으면 일일 기본 식비를 반환하는 MealExpense 객체 반환<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PerDiemMealExpenses</span> <span class="keyword">implements</span> <span class="title class_">MealExpenses</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getTotal</span><span class="params">()</span> {</span><br><span class="line">      <span class="comment">// return the per diem default</span></span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// ex??</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExpenseReportDAO</span> {</span><br><span class="line">    ...</span><br><span class="line">    <span class="keyword">public</span> MealExpenses <span class="title function_">getMeals</span><span class="params">(<span class="type">int</span> employeeId)</span> {</span><br><span class="line">      MealExpenses expenses;</span><br><span class="line">      <span class="keyword">try</span> {</span><br><span class="line">        expenses = expenseReportDAO.getMeals(employee.getID());</span><br><span class="line">      } <span class="keyword">catch</span>(MealExpensesNotFound e) {</span><br><span class="line">        expenses = <span class="keyword">new</span> <span class="title class_">PerDiemMealExpenses</span>();</span><br><span class="line">      }</span><br><span class="line">      </span><br><span class="line">      <span class="keyword">return</span> expenses;</span><br><span class="line">    }</span><br><span class="line">    ...</span><br><span class="line">  }</span><br></pre></td></tr></tbody></table></figure></li><li><a href="https://martinfowler.com/eaaCatalog/specialCase.html">특수 사례 패턴</a><ul><li>클래스를 만들거나 객체를 조작해서 특수 사례를 처리하는 방식</li><li>객체나 클래스가 예외적인 상황을 캡슐화해서 처리</li><li>클라이언트 코드가 예외적인 상황을 처리할 필요가 없어진다</li></ul></li></ul><h2 id="null을-반환하지-마라"><a href="#null을-반환하지-마라" class="headerlink" title="null을 반환하지 마라"></a>null을 반환하지 마라</h2><ul><li><p>null확인이 가득한 코드 → 나쁜 코드</p><ul><li>null 반환 코드는 일거리를 늘리며 호출자에게 문제를 넘김</li><li>한명이라도 null 체크 빼먹으면 위험<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerItem</span><span class="params">(Item item)</span> {</span><br><span class="line">    <span class="keyword">if</span> (item != <span class="literal">null</span>) {</span><br><span class="line">      <span class="type">ItemRegistry</span> <span class="variable">registry</span> <span class="operator">=</span> peristentStore.getItemRegistry();</span><br><span class="line">      <span class="keyword">if</span> (registry != <span class="literal">null</span>) {</span><br><span class="line">        <span class="type">Item</span> <span class="variable">existing</span> <span class="operator">=</span> registry.getItem(item.getID());</span><br><span class="line">        <span class="keyword">if</span> (existing.getBillingPeriod().hasRetailOwner()) {</span><br><span class="line">          existing.register(item);</span><br><span class="line">        }</span><br><span class="line">      }</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line"><span class="comment">// - peristentStore가 null인 경우 예외처리 안됨</span></span><br><span class="line"><span class="comment">// - 안된 것도 찾기가 힘든</span></span><br></pre></td></tr></tbody></table></figure></li></ul></li><li><p>메서드에서 null을 반환하고 픈 유혹이 든다면? → 예외를 던지거나 특수 사례 객체를 반환하라</p></li><li><p>사용하려는 외부 api가 null 반환? → 감싸기 기법 사용 → 예외 던지거나 특수 사례 객체 반환하는 방식 고려</p></li><li><p>많은 경우 특수 사례 객체가 손쉬운 해결책</p><figure class="highlight java"><figcaption><span>예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. null 반환</span></span><br><span class="line">List&lt;Employee&gt; employees = getEmployees();</span><br><span class="line">  <span class="keyword">if</span> (employees != <span class="literal">null</span>) { </span><br><span class="line">    <span class="keyword">for</span>(Employee e : employees) {</span><br><span class="line">      totalPay += e.getPay();</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line"><span class="comment">// 2. null 체크 없애고 getEmployees에서 빈 리스트 반환</span></span><br><span class="line">List&lt;Employee&gt; employees = getEmployees();</span><br><span class="line">  <span class="keyword">for</span>(Employee e : employees) {</span><br><span class="line">    totalPay += e.getPay();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> List&lt;Employee&gt; <span class="title function_">getEmployees</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">if</span>( .. there are no employees .. )</span><br><span class="line">      <span class="keyword">return</span> Collections.emptyList();</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 코드도 깔끔해지고 nullPointerException 발생도 줄어든다 - 안정화</span></span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="null-전달하지마라"><a href="#null-전달하지마라" class="headerlink" title="null 전달하지마라"></a>null 전달하지마라</h2><ul><li>메서드 null 반환보다 메서드 null 전달이 더 나쁨</li><li>정상적인 인수로 null을 기대하는 API가 아니라면 메서드로 null 전달하는 코드는 최대한 피한다</li><li>일반적으로 대다수의 프로그래밍 언어들은 파라미터로 들어온 null에 대해 적절한 방법을 제공하지 못한다.</li><li>가장 이성적인 해법은 null을 파라미터로 받지 못하게 하는 것이다.</li></ul><figure class="highlight java"><figcaption><span>null이 전달되면 NullPointerException 발생!</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MetricsCalculator</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">xProjection</span><span class="params">(Point p1, Point p2)</span> {</span><br><span class="line">    <span class="keyword">return</span> (p2.x – p1.x) * <span class="number">1.5</span>;</span><br><span class="line">  }</span><br></pre></td></tr></tbody></table></figure><p>어떻게 고치는가?</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 새로운 예외 유형을 만들어 던지기</span></span><br><span class="line"><span class="comment">// NullPointerException은 안나지만 InvalidArgumentException를 잡아내는 처리기 필요</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MetricsCalculator</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">xProjection</span><span class="params">(Point p1, Point p2)</span> {</span><br><span class="line">    <span class="keyword">if</span>(p1 == <span class="literal">null</span> || p2 == <span class="literal">null</span>){</span><br><span class="line">      <span class="keyword">throw</span> InvalidArgumentException(<span class="string">"Invalid argument for MetricsCalculator.xProjection"</span>);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> (p2.x – p1.x) * <span class="number">1.5</span>;</span><br><span class="line">  }</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 2. assert문 사용</span></span><br><span class="line"><span class="comment">// 문서화가 잘되어 코드 가독성은 좋으나 문제 해결은 여전함</span></span><br><span class="line"><span class="comment">// null 전달시 실행오류 여전함</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MetricsCalculator</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">xProjection</span><span class="params">(Point p1, Point p2)</span> {</span><br><span class="line">    <span class="keyword">assert</span> p1 != <span class="literal">null</span> : <span class="string">"p1 should not be null"</span>;</span><br><span class="line">    <span class="keyword">assert</span> p2 != <span class="literal">null</span> : <span class="string">"p2 should not be null"</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> (p2.x – p1.x) * <span class="number">1.5</span>;</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>null 인수의 자체가 문제</p><ul><li>대다수 프로그래밍 언어는 호출자가 실수로 넘기는 null을 적절히 처리하는 방법이 없음</li><li>애초에 null을 넘기지 못하도록 금지하는 정책이 함리적</li><li>인수로 null이 넘어오면 코드에 문제가 있는 것</li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><ul><li>클린코드는 읽기도 좋지만 안정성도 좋야야 하며 상충하는 목표가 아님</li><li>오류처리를 프로그램 로직과 분리해서 독자적 사안으로 고려 → 튼튼한 클린코드 작성 가능</li><li>독립적 추론 가능 , 코드 유지보수성 올라감</li></ul><h2 id="실무-예외-처리-패턴"><a href="#실무-예외-처리-패턴" class="headerlink" title="실무 예외 처리 패턴"></a>실무 예외 처리 패턴</h2><blockquote><p>제로베이스에서 한달 한권의 내용</p></blockquote><h3 id="1-getOrElse"><a href="#1-getOrElse" class="headerlink" title="1. getOrElse"></a>1. getOrElse</h3><ul><li>예외 대신 기본 값 리턴<ol><li>null이 아닌 기본값 : 위의 employee 예제</li><li>도메인에 맞는 기본 값 : 아래 설명</li></ol></li></ul><figure class="highlight java"><figcaption><span>나쁜 예</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">UserLevel</span> <span class="variable">userLevel</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line">  <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> user.Repository.findByUserId(userId);</span><br><span class="line">  userLevel = user.getUserLevel();</span><br><span class="line">} <span class="keyword">catch</span> (UserNotfoundException e) {</span><br><span class="line">  userLevel = UserLevel.BASIC;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 호출부에서 예외 처리를 통해 userLevel 값을 처리한다</span></span><br><span class="line"><span class="comment">// 코드를 계속 읽어나가면서 논리적인 흐름이 끊긴다</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>좋은 예</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 호출부 - 단순해짐</span></span><br><span class="line"><span class="type">UserLevel</span> <span class="variable">userLevel</span> <span class="operator">=</span> userService.getuserLevelOrDefault(userId);</span><br><span class="line"><span class="comment">//////////////////////////////////////////////////</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">UserLevel</span> <span class="variable">USER_BASIC_LEVEL</span> <span class="operator">=</span> UsrLevel.BASIC;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> UserLevel <span class="title function_">getUserLevelOrdefault</span><span class="params">(Long userId)</span> {</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userRepository.findByUserId(userId);</span><br><span class="line">      <span class="keyword">return</span> user.getUserLevel();</span><br><span class="line">    } <span class="keyword">catch</span>(UserNotFoundException e) {</span><br><span class="line">      <span class="keyword">return</span> USER_BASIC_LEVEL;</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>예외처리를 데이터 제공 쪽 처리 → 호출부가 심플, 단순해짐</li><li>코드 리딩시 논리적 흐름이 끊기지 않음</li><li>도메인에 맞는 default 값을 도메인 서비스에서 관리</li></ul><h3 id="getOrElseThrow"><a href="#getOrElseThrow" class="headerlink" title="getOrElseThrow"></a>getOrElseThrow</h3><h4 id="기본값이-없으면-null대신-예외를-던진다"><a href="#기본값이-없으면-null대신-예외를-던진다" class="headerlink" title="기본값이 없으면 null대신 예외를 던진다"></a>기본값이 없으면 null대신 예외를 던진다</h4><figure class="highlight java"><figcaption><span>나쁜 예</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userRepository.findByUseId(userId);</span><br><span class="line"><span class="keyword">if</span> (user != <span class="literal">null</span>) {</span><br><span class="line">  <span class="comment">// user를 이용한 처리</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>user를 사용하는 쪽에서 매번 체크를 해야한다.</li><li>가독성 뿐만 아니라 안정성도 떨어진다.</li><li>null 체크가 빠진 부분이 발생할 수 있다.</li></ul><figure class="highlight java"><figcaption><span>좋은 예</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 호출부</span></span><br><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> user.Service.getUserOrElseThrow(userId);</span><br><span class="line"><span class="comment">//////////////////////////////////</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserService</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">UserLevel</span> <span class="variable">USER_BASIC_LEVEL</span> <span class="operator">=</span> UsrLevel.BASIC;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> User <span class="title function_">getUserOrElseThrow</span><span class="params">(Long userId)</span> {</span><br><span class="line">    <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userRepository.findByUserId(userId);</span><br><span class="line">    <span class="keyword">if</span> (user == <span class="literal">null</span>) {</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">"User is not found. userId = "</span> + userId)</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> user;</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>기본 값이 없는 경우임</li><li>기본 값이 없으면 null 체크 →  데이터가 없으면 예외 던짐</li><li>호출부에서 매번 null 체크가 필요 없음</li><li>호출부의 가독성 올라감</li></ul><h4 id="실무에선-보통-자신의-예외를-정의"><a href="#실무에선-보통-자신의-예외를-정의" class="headerlink" title="실무에선 보통 자신의 예외를 정의"></a>실무에선 보통 자신의 예외를 정의</h4><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyProjectException</span> <span class="keyword">extends</span> <span class="title class_">RuntimeException</span> {</span><br><span class="line">  <span class="keyword">private</span> MyErrorCode errorCode;</span><br><span class="line">  <span class="keyword">private</span> String errorMessage;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">MyProjectException</span><span class="params">(MyErrorCode errorCode)</span> {</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">MyProjectException</span><span class="params">(MyErrorCode errorCode, String errorMessage)</span> {</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">  }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">MyErrorCode</span> {</span><br><span class="line">  <span class="keyword">private</span> String defaultErrorMessage;</span><br><span class="line">  </span><br><span class="line">  INVALID_REQUEST(<span class="string">"잘못된 요청입니다."</span>),</span><br><span class="line">  DUPLICATED_REQUEST(<span class="string">"기존 요청과 중복되어 처리할 수 없습니다."</span>),</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  INTERNAL_SERVER_ERROR(<span class="string">"처리 중 에러가 발생했습니다."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//호출부 - 클라이언트가 아닌 내부 null 처리 부분</span></span><br><span class="line"><span class="keyword">if</span> (request.getUserName() == <span class="literal">null</span>) {</span><br><span class="line">  <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">MyProjectException</span>(ErrorCode.INVALID_REQUEST, <span class="string">"userName is null"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>장점</p><ul><li>에러 로그에서 stacktrace할 때 자신의 예외라는 것을 바로 인지 가능</li><li>다른 라이브러리 에러와 섞이지 않음 : 다른 IllegalArgumentException과 분리</li><li>우리 도메인에서 발생한 에러 종류를 나열 가능</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch7/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅵ. 객체와 자료구조</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch6/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch6/</guid>
      <pubDate>Wed, 09 Feb 2022 00:53:10 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅵ-객체와-자료구조&quot;&gt;&lt;a href=&quot;#Ⅵ-객체와-자료구조&quot; class=&quot;headerlink&quot; title=&quot;Ⅵ. 객체와 자료구조</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅵ-객체와-자료구조"><a href="#Ⅵ-객체와-자료구조" class="headerlink" title="Ⅵ. 객체와 자료구조"></a>Ⅵ. 객체와 자료구조</h1><h2 id="Intro"><a href="#Intro" class="headerlink" title="Intro"></a>Intro</h2><blockquote><p>변수를 비공개private로 정의하는 이유가 있다. 남들이 변수에 의존하지 않게 만들고 싶어서다. 충동이든 변덕이든, 변수 타입이나 구현을 맘대로 바꾸고 싶어서다. 그렇다면 어째서 수많은 프로그래머가 조회get 함수와 설정set 함수를 당연하게 공개public해 비공개 변수를 외부에 노출할까?</p></blockquote><ul><li>인트로가 괜춘하다</li><li>한번 생각해보자.. 일단 캡슐화</li><li>캡슐화 - 외부에 들어나지 않도록</li><li>다수가 함께 작업하는 프로젝트의 경우 해당 자료를 보호할 수 있도록 자료 보호</li></ul><h2 id="자료-추상화"><a href="#자료-추상화" class="headerlink" title="자료 추상화"></a>자료 추상화</h2><figure class="highlight java"><figcaption><span>6-1 구체적 Point 클래스 : 구현 회부 노출</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Point</span> { </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> x; </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> y;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>6-2 추상적인 Point 클래스 : 구현을 완전히 숨긴다</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Point</span> {</span><br><span class="line">  <span class="type">double</span> <span class="title function_">getX</span><span class="params">()</span>;</span><br><span class="line">  <span class="type">double</span> <span class="title function_">getY</span><span class="params">()</span>;</span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">setCartesian</span><span class="params">(<span class="type">double</span> x, <span class="type">double</span> y)</span>; </span><br><span class="line">  <span class="type">double</span> <span class="title function_">getR</span><span class="params">()</span>;</span><br><span class="line">  <span class="type">double</span> <span class="title function_">getTheta</span><span class="params">()</span>;</span><br><span class="line">  <span class="keyword">void</span> <span class="title function_">setPolar</span><span class="params">(<span class="type">double</span> r, <span class="type">double</span> theta)</span>; </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>인터페이스 포인트(6-2)</p><ul><li>어떤 좌표계인지는 알 수가 없다</li><li>자료구조를 명백히 표현하며 그 이상도 표현한다<ul><li>클래스 메서드가 접근 정책 강제</li><li>좌표를 읽을 때는 각 값을 개별적으로 읽어야 한다</li><li>좌표 설정시에는 두 값을 한꺼번에 설정해야 가능</li></ul></li></ul><p>클래스 포인트(6-1)</p><ul><li>확실하게 직교좌표계를 사용</li><li>개별적으로 좌표값 읽고 설정하게 강제</li><li>구현을 노출 : 변수는 private인데 getter,setter가 있어도 마찬가지로 외부 노출로 본다</li></ul><h4 id="6-2-gt-6-1"><a href="#6-2-gt-6-1" class="headerlink" title="6-2 > 6.1"></a>6-2 &gt; 6.1</h4><p>진정한 클래스</p><ul><li>형식 논리에 치우쳐 조회 함수와 설정 함수로 변수를 다룬다고 클래스가 되지 않는다</li><li>변수 사이에 함수 계층이 들어간다고 구현이 저절로 감춰지지 않는다</li><li>진정한 의미의 클래스 : 추상 인터페이스를 제공해서 사용자가 구현을 모르는 채 자료의 핵심 조작 가능</li><li>추상적인 개념으로 표현 &gt;&gt;&gt; 자료 세부 공개하기</li><li>인터페이스나 getter,setter 설정으로 추상화가 되지 않음</li><li>생각없는 getter/setter를 추가하는 방법이 가장 나쁘다</li><li>개발자는 객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다</li></ul><h2 id="자료-x2F-객체-비대칭"><a href="#자료-x2F-객체-비대칭" class="headerlink" title="자료/객체 비대칭"></a>자료/객체 비대칭</h2><p>특징 : 사소한 차이지만 영향은 엄청남</p><ul><li>객체 : 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개</li><li>자료 구조 : 자료를 그대로 공개하며 별다른 함수를 제공하지 않음</li></ul><figure class="highlight java"><figcaption><span>procedural Shape</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Square</span> { </span><br><span class="line">  <span class="keyword">public</span> Point topLeft; </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> side;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Rectangle</span> { </span><br><span class="line">  <span class="keyword">public</span> Point topLeft; </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> height; </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> width;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Circle</span> { </span><br><span class="line">  <span class="keyword">public</span> Point center; </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> radius;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Geometry</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">double</span> <span class="variable">PI</span> <span class="operator">=</span> <span class="number">3.141592653589793</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">area</span><span class="params">(Object shape)</span> <span class="keyword">throws</span> NoSuchShapeException {</span><br><span class="line">    <span class="keyword">if</span> (shape <span class="keyword">instanceof</span> Square) { </span><br><span class="line">      <span class="type">Square</span> <span class="variable">s</span> <span class="operator">=</span> (Square)shape; </span><br><span class="line">      <span class="keyword">return</span> s.side * s.side;</span><br><span class="line">    } <span class="keyword">else</span> <span class="keyword">if</span> (shape <span class="keyword">instanceof</span> Rectangle) { </span><br><span class="line">      <span class="type">Rectangle</span> <span class="variable">r</span> <span class="operator">=</span> (Rectangle)shape; </span><br><span class="line">      <span class="keyword">return</span> r.height * r.width;</span><br><span class="line">    } <span class="keyword">else</span> <span class="keyword">if</span> (shape <span class="keyword">instanceof</span> Circle) {</span><br><span class="line">      <span class="type">Circle</span> <span class="variable">c</span> <span class="operator">=</span> (Circle)shape;</span><br><span class="line">      <span class="keyword">return</span> PI * c.radius * c.radius; </span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoSuchShapeException</span>(); </span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>procedural Shape</p><ul><li>GeoMetry 클래스는 3가지 도형 클래스 다룬다</li><li>각 클래스는 간단한 자료 구조, 메서드 X</li><li>도형이 동작하는 방식 GeoMetry클래스에서 구현</li><li>객체 지향에서는 상상할 수 없는 클래스</li><li>변화시?<ul><li>Geometry에 둘레 길이 구하는 함수 추가? → 3가지 도형 클래스는 아무 영향이 없음</li><li>반대로 새 도형을 추가하고 싶다면? → Geomtry의 함수를 모두 고쳐야 함</li></ul></li></ul><figure class="highlight java"><figcaption><span>Polymorphic Shape</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Square</span> <span class="keyword">implements</span> <span class="title class_">Shape</span> { </span><br><span class="line">  <span class="keyword">private</span> Point topLeft;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">double</span> side;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">area</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> side * side;</span><br><span class="line">  } </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Rectangle</span> <span class="keyword">implements</span> <span class="title class_">Shape</span> { </span><br><span class="line">  <span class="keyword">private</span> Point topLeft;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">double</span> height;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">double</span> width;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">area</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> height * width;</span><br><span class="line">  } </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Circle</span> <span class="keyword">implements</span> <span class="title class_">Shape</span> { </span><br><span class="line">  <span class="keyword">private</span> Point center;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">double</span> radius;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">double</span> <span class="variable">PI</span> <span class="operator">=</span> <span class="number">3.141592653589793</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">area</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">return</span> PI * radius * radius;</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>Polymorphic Shape</p><ul><li>객체지향적</li><li>area() : polymorphic method</li><li>Geomtry클래스 필요없음</li><li>변화시?<ul><li>새 도형을 추가하고 싶다면? → 기존 함수 영향 zero</li><li>Geometry에 둘레 길이 구하는 함수 추가? → 3가지 도형 클래스 모두 뜯어 고쳐야 함</li></ul></li></ul><blockquote><p>해결법 ?VISIOR 패턴?</p><ul><li>주로 상속없이 클래스에 메서드를 효과적으로 추가하기 위해 사용</li><li>합성 객체의 내부 구조가 VISITOR에 Open됨 → 캡슐화 위반 문제점</li></ul></blockquote><p>procedural Shape VS Polymorphic Shape<br>상호 보완적이며 사실상 반대 → 객체와 자료구조는 근본적으로 양분됨<br>서로의 장점</p><ul><li>(자료구조를 사용하는) 절차적 코드 : 기존 자료 구조 변경 없이 새함수 추가</li><li>OOP 코드 : 기존 함수 변경 없이 새 클래스 추가 쉬움<br>서로의 단점</li><li>(자료구조를 사용하는) 절차적 코드 : 새로운 자료 추가 어려움(모든 함수 수정 필요)</li><li>OOP 코드 : 새로운 함수를 추가하기  어려움(모든 클래스를 고쳐야 함)<br>결론</li><li>모든 것이 객체라는 생각은 미신</li><li>때로는 단순한 자료 구조+절차적 코드가 가장 적합한 상황도 존재</li></ul><h2 id="디미터-법칙"><a href="#디미터-법칙" class="headerlink" title="디미터 법칙"></a>디미터 법칙</h2><blockquote><p>상세 내용은 실용주의 프로그래머 227 p</p></blockquote><ul><li>잘 알려진 휴리스틱<ul><li>휴리스틱 : 경험에 기반하여 문제를 해결하거나 학습하거나 발견해 내는 방법</li></ul></li><li><strong>모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다</strong>  <ul><li>객체는 자료를 숨기고 함수를 공개</li><li>즉 조회 함수로 내부 구조를 공개하면 안된다는 의미</li><li>ex) 모든 getter 사용? 내부 구조가 노출되므로 안된다는 뜻</li></ul></li><li>클래스 C의 메서드 f는 다음과 같은 객체의 메서드만 호출해야 한다<ul><li>클래스 C</li><li>f가 생성한 객체</li><li>f 인수로 넘어온 객체</li><li>C 인스턴스 변수에 저장된 객체</li><li>주의: 위 객체에서 허용된 메서드가 반환하는 객체의 메서드는 호출 X</li></ul></li></ul><h3 id="train-wreck-x3D-기차-충돌"><a href="#train-wreck-x3D-기차-충돌" class="headerlink" title="train wreck = 기차 충돌"></a>train wreck = 기차 충돌</h3><figure class="highlight java"><figcaption><span>아파치 프레임워크의 train wreck처럼 보이는 함수</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="type">String</span> <span class="variable">outputDir</span> <span class="operator">=</span> ctxt.getOptions().getScratchDir().getAbsolutePath();</span><br></pre></td></tr></tbody></table></figure><ul><li><p>위와 같은 코드는 디미터 법칙이 안지켜지는 것으로 보여짐</p></li><li><p>ctxt가 Options를 포함 → Options가 ScratchDir 포함 → ScratchDir이 AbsolutePath를 포함<br>→함수가 하나가 아는 지식이 굉장히 많음 → 해당 함수가 많은 객체를 탐색 가능함</p><figure class="highlight java"><figcaption><span>이렇게 나누는 것이 좋다</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Options</span> <span class="variable">opts</span> <span class="operator">=</span> ctxt.getOptions();</span><br><span class="line"><span class="type">File</span> <span class="variable">scratchDir</span> <span class="operator">=</span> opts.getScratchDir();</span><br><span class="line"><span class="keyword">final</span> <span class="type">String</span> <span class="variable">outputDir</span> <span class="operator">=</span> scratchDir.getAbsolutePath();</span><br></pre></td></tr></tbody></table></figure><p>첫번째 예제가 디미터 법칙을 위반했는가?</p></li><li><p>ctx, Options, ScratchDir이 객체인가 자료구조인지에 따라 달림</p><ul><li>객체라면? 내부구조를 숨겨야하므로 확실하게 디미터 법칙 위반</li><li>자료구조라면? 당연히 내부 구조를 노출해야함 → 디미터 법칙 적용 안됨</li></ul></li><li><p>첫번째 예제의 혼란의 이유? Getter 사용 덕분</p><figure class="highlight java"><figcaption><span>이런식으로 구현되면 디미터 법칙을 거론할 필요가 없다</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="type">String</span> <span class="variable">outputDir</span> <span class="operator">=</span> ctxt.options.scratchDir.absolutePath;</span><br></pre></td></tr></tbody></table></figure></li><li><p>자료구조는 무조건 함수없이 공개 변수만 포함, 객체는 비공개 변수+공개함수를 포함하면 문제가 간단해진다  ‘</p></li></ul><h3 id="잡종구조"><a href="#잡종구조" class="headerlink" title="잡종구조"></a>잡종구조</h3><ul><li>단순한 자료 구조에도 getter,setter를 요구하는 프레임워크나 표준(bean등;)이 존재 → 원흉</li><li>이런 혼란으로 때때로 절반은 객체, 절반은 자료구조인 잡종 구조가 등장</li><li>잡종 구조는 중요기능 수행 함수 + 공개변수 + getter,setter도 존재</li><li>getter,setter덕에 미공개 변수를 사용하고픈 유혹에 빠지기 쉬움 → 리팩토링 책에서는 기능 욕심(Feature Envy)라고 함</li><li>새로운 함수는 물론이고 새로운 자료구조도 추가하기 어려움 → 양쪽의 단점만 모아놓은 구조</li><li><strong>잡종 구조는 되도록 피하는 편이 좋다</strong></li><li>프로그그래머 함수/타입을 보호할지 공개할지 확신하지 못하거나(아니면 무지해서) 어중간하게 내놓은 설계에 불과</li></ul><h3 id="구조체-감추기"><a href="#구조체-감추기" class="headerlink" title="구조체 감추기"></a>구조체 감추기</h3><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//1</span></span><br><span class="line">ctxt.getAbsolutePathOfScratchDirectoryOption();</span><br><span class="line"><span class="comment">//2</span></span><br><span class="line">ctxt.getScratchDirectoryOption().getAbsolutePath();</span><br></pre></td></tr></tbody></table></figure><p>문제점 : 둘 다 좋은 방법이 아님</p><ul><li>1번은 ctxt객체에 공개해야하는 메서드가 너무 많아짐</li><li>2번은 <code>getScratchDirectoryOption()</code>가 객체가 아닌 자료구조 반환임을 가정</li></ul><p>ctxt가 객체라면 <strong>무언가를 해라</strong> 라고 해야지 속을 드러내라고 하면 안됨</p><ul><li>임시 디렉토리의 절대경로가 왜 필요한가? 절대경로를 얻어 어디에 쓸것인가?<figure class="highlight java"><figcaption><span>한참 아래의 같은 모듈에서 가져온 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 추상화 수준이 뒤섞여서 불편</span></span><br><span class="line"><span class="comment">// 점과 슬래시와 파일 확장자등이 부주의하게 섞여있음</span></span><br><span class="line"><span class="type">String</span> <span class="variable">outFile</span> <span class="operator">=</span> outputDir + <span class="string">"/"</span> + className.replace(<span class="string">'.'</span>, <span class="string">'/'</span>) + <span class="string">".class"</span>; </span><br><span class="line"><span class="type">FileOutputStream</span> <span class="variable">fout</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(outFile); </span><br><span class="line"><span class="type">BufferedOutputStream</span> <span class="variable">bos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedOutputStream</span>(fout);</span><br><span class="line"><span class="comment">// 결론 : 임시파일을 생성하기 위해 절대 경로가 필요함</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>ctxt 객체에 임시 파일을 생성하라고 시키면 어떨까?</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">BufferedOutputStream</span> <span class="variable">bos</span> <span class="operator">=</span> ctxt.createScratchFileStream(classFileName);</span><br></pre></td></tr></tbody></table></figure></li><li>객체에 맡기기 적당한 임무로 보임</li><li>ctxt는 내부 구조를 드러내지 않으며, 모듈은 자신이 몰라야 하는 여러 객체를 탐색할 필요가 없다.</li><li>따라서 디미터 법칙을 위반하지 않는다.</li></ul><h2 id="자료-전달-객체"><a href="#자료-전달-객체" class="headerlink" title="자료 전달 객체"></a>자료 전달 객체</h2><p>자료 구조의 전형적인 형태 : DTO</p><ul><li>공개 변수만 존재 , 함수 없음</li><li>DTO라고도 한다</li><li>굉장히 유용한 구조체 : DB 통신, 소켓 메시지 구문 통신</li><li>흠히 DB의 가공되지 않은 정보를 어플리케이션 코드의 객체로 변환과정에서 가장 처음으로 사용하는 구조</li><li>getter/setter를 가진다</li></ul><p>좀더 일반적인 형태  : bean 구조</p><ul><li>데이터 표현이 목적인 자바 객체</li><li>private 변수 + getter, setter</li><li>일종의 사이비 캡슐화, 별다른 이득이 없음</li><li>zh</li></ul><h3 id="활성-레코드-DTO의-특수-형태"><a href="#활성-레코드-DTO의-특수-형태" class="headerlink" title="활성 레코드 : DTO의 특수 형태"></a>활성 레코드 : DTO의 특수 형태</h3><ul><li>위의 dto나 bean구조의 자료구조 + 탐색 함수(save or find) 제공</li><li>DB 테이블이나 다른 소스에서 자료를 직접 변환한 결과 → DB row를 객체에 매핑하는 패턴</li><li>엔티티와 다르게 생겼음~</li><li>불행히도 활성 레코드에 비지니스 규칙 메서드를 추가한 자료구조를 객체 취급하는 개발자가 흔함</li><li>잡종 구조가 나오므로 바람직하지 않음</li><li>해결책<ul><li>활성 레코드는 자료 구조로 취급해야한다</li><li>비지니스 규칙을 담으면서 내부 자료를 숨기는 객체(엔티티등)는 따로 생성</li></ul></li><li>객체가 많아지면 복잡해짐, 가까운 곳에 관련 로직이 있는 것이 좋음<br>→ 현업에서는 엔티티에 간단한 메서드를 추가해 사용<br>→ 현업에서는 엔티티에 간단한 메서드를 추가해 사용</li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><p>객체는 동작을 공개하고 자료를 숨긴다.<br>그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기는 쉬운 반면, 기존 객체에 새 동작을 추가하기는 어렵다.</p><p>자료 구조는 별다른 동작 없이 자료를 노출한다.<br>그래서 기존 자료 구조에 새 동작을 추가하기는 쉬우나, 기존 함수에 새 자료 구조를 추가하기는 어렵다.</p><ul><li>(어떤) 시스템을 구현할 때, 새로운 자료 타입을 추가하는 유연성이 필요하면 객체가 더 적합하다.</li><li>다른 경우로 새로운 동작을 추가하는 유연성이 필요하면 자료 구조와 절차적인 코드가 더 적합하다.</li><li>우수한 소프트웨어 개발자는 편견 없이 이 사실을 이해해 직면한 문제에 최적인 해결책을 선택한다.</li></ul><blockquote><ul><li>김과장이 햇갈려서 질의 응답에 많은 시간을 허비<ul><li>디미터의 법칙을 객체가 아닌 메서드의 관점으로 착각해서 체인메서드들을 쓰면 안되는 것으로 착각함</li><li>객체의 관점으로 봐야 함을 설명하는데도 이해를 못해서 많은 시간 허비</li></ul></li><li>다음 번 알려줄 때에는 해당 부분을 햇갈리지 않도록 부연 설명을 미리 준비 해야 할 듯</li></ul></blockquote><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch6/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅴ. 형식 맞추기</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch5/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch5/</guid>
      <pubDate>Wed, 09 Feb 2022 00:52:42 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅴ-형식-맞추기-Formatting&quot;&gt;&lt;a href=&quot;#Ⅴ-형식-맞추기-Formatting&quot; class=&quot;headerlink&quot; </description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅴ-형식-맞추기-Formatting"><a href="#Ⅴ-형식-맞추기-Formatting" class="headerlink" title="Ⅴ.형식 맞추기(Formatting)"></a>Ⅴ.형식 맞추기(Formatting)</h1><h3 id="목적"><a href="#목적" class="headerlink" title="목적"></a>목적</h3><p>코드 형식은 중요하다</p><ul><li>너무 중요하지만 융통성 없이 맹목적으로 따르면 안된다</li><li>코드 형식은 의사소통의 일환이며 개발자의 1차적 의무</li><li>돌아가는 코드가 1차적인 의무가 아님</li><li>오늘 구현한 코드의 가독성은 앞으로의 코드 품질에 큰 영향</li><li>원래 코드는 사라져도 스타일과 규율은 사라지지 않고 유지보수에 계속 영향을 미친다</li></ul><h3 id="적절한-행-길이를-유지하라"><a href="#적절한-행-길이를-유지하라" class="headerlink" title="적절한 행 길이를 유지하라"></a>적절한 행 길이를 유지하라</h3><blockquote><p>책에서는 여러 프로젝트의 파일 크기를 조사한 결과를 보여준다<br>대표적인 예시 - FitNesse</p></blockquote><ul><li>5만 라인이 넘는 시스템</li><li>500줄이 넘어가는 파일 없으며, 대다수 200줄 미만<br><strong>일반적으로 큰 파일 보다 작은 파일이 이해가 쉽다</strong></li></ul><h4 id="신문기사처럼-작성하라"><a href="#신문기사처럼-작성하라" class="headerlink" title="신문기사처럼 작성하라"></a>신문기사처럼 작성하라</h4><p>좋은 신문 기사의 모습</p><ul><li>최상단에 기사를 몇 마디로 요약하는 표제</li><li>독자는 위 아래로 기사를 읽으며 표제를 읽고 기사를 읽을지 결정</li><li>첫 문단 : 전체 기사 내용 요약, 세부사항 숨기고 큰 그림 위주로 보여줌</li><li>쭉 읽어 나가면? : 이름, 발언, 주장등 세부사항이 드러남</li></ul><p>소스파일도 비슷하게 작성</p><ul><li>이름 : 간단하면서도 이름만 보고 올바른 모듈인지 판단되게 신경써서 짓는다</li><li>소스 파일 첫 부분 : 고차원 개념과 알고리즘 설명</li><li>아래로 내려갈 수록 의도가 세세하게 묘사됨</li><li>마지막에 가장 저차원 함수와 세부내역</li></ul><p>생각해볼 점</p><ul><li>신문은 다양한 기사로 이루어짐</li><li>대다수 기사가 아주 짧으며 어떤 기사는 조금 길며 한 면을 채우는 기사는 매우 드물다</li><li>신문이 세부내ㅔ역을 무작위로 섞은 긴 기사 하나만 싣는다면 아무도 읽지 않는다</li><li>소스코드와 비교하면서 생각해보자</li></ul><h4 id="개념은-빈-행으로-분리"><a href="#개념은-빈-행으로-분리" class="headerlink" title="개념은 빈 행으로 분리"></a>개념은 빈 행으로 분리</h4><ul><li>거의 모든 코드의 읽는 순서 : 왼쪽→오른쪽, 위→아래</li><li>각 줄 : 수식이나 절을 나타낸다</li><li>여러 줄의 묶음 : 완결된 생각 하나</li><li>생각 사이에는 빈 행을 넣어 분리해야 한다</li><li>빈 행이 없으면 가독성이 떨어진다<figure class="highlight java"><figcaption><span>빈 행 없을 때</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> fitnesse.wikitext.widgets;</span><br><span class="line"><span class="keyword">import</span> java.util.regex.*;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BoldWidget</span> <span class="keyword">extends</span> <span class="title class_">ParentWidget</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">REGEXP</span> <span class="operator">=</span> <span class="string">"'''.+?'''"</span>;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Pattern</span> <span class="variable">pattern</span> <span class="operator">=</span> Pattern.compile(<span class="string">"'''(.+?)'''"</span>,</span><br><span class="line">    Pattern.MULTILINE + Pattern.DOTALL);</span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">BoldWidget</span><span class="params">(ParentWidget parent, String text)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="built_in">super</span>(parent);</span><br><span class="line">    <span class="type">Matcher</span> <span class="variable">match</span> <span class="operator">=</span> pattern.matcher(text); match.find(); </span><br><span class="line">    addChildWidgets(match.group(<span class="number">1</span>));}</span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">render</span><span class="params">()</span> <span class="keyword">throws</span> Exception { </span><br><span class="line">    <span class="type">StringBuffer</span> <span class="variable">html</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuffer</span>(<span class="string">"&lt;b&gt;"</span>); </span><br><span class="line">    html.append(childHtml()).append(<span class="string">"&lt;/b&gt;"</span>); </span><br><span class="line">    <span class="keyword">return</span> html.toString();</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>빈 행 있을 때</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> fitnesse.wikitext.widgets;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.util.regex.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BoldWidget</span> <span class="keyword">extends</span> <span class="title class_">ParentWidget</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">REGEXP</span> <span class="operator">=</span> <span class="string">"'''.+?'''"</span>;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Pattern</span> <span class="variable">pattern</span> <span class="operator">=</span> Pattern.compile(<span class="string">"'''(.+?)'''"</span>, </span><br><span class="line">    Pattern.MULTILINE + Pattern.DOTALL</span><br><span class="line">  );</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">BoldWidget</span><span class="params">(ParentWidget parent, String text)</span> <span class="keyword">throws</span> Exception { </span><br><span class="line">    <span class="built_in">super</span>(parent);</span><br><span class="line">    <span class="type">Matcher</span> <span class="variable">match</span> <span class="operator">=</span> pattern.matcher(text);</span><br><span class="line">    match.find();</span><br><span class="line">    addChildWidgets(match.group(<span class="number">1</span>)); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">render</span><span class="params">()</span> <span class="keyword">throws</span> Exception { </span><br><span class="line">    <span class="type">StringBuffer</span> <span class="variable">html</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuffer</span>(<span class="string">"&lt;b&gt;"</span>); </span><br><span class="line">    html.append(childHtml()).append(<span class="string">"&lt;/b&gt;"</span>); </span><br><span class="line">    <span class="keyword">return</span> html.toString();</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h4 id="세로-밀집도"><a href="#세로-밀집도" class="headerlink" title="세로 밀집도"></a>세로 밀집도</h4><ul><li>줄바꿈이 개념 분리라면? 세로 밀집도는 연관성을 의미</li><li>밀접한 코드행은 세로로 가까이 놓여야 한다</li><li>연관성이 있는 부분을 주석등의 추가로 멀리 떨어뜨리면 한 눈에 들어오지 않게 된다</li></ul><figure class="highlight java"><figcaption><span>연관성 부분에 주석이 들어가 변수끼리 멀어진 경우</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">ublic <span class="keyword">class</span> <span class="title class_">ReporterConfig</span> {</span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">  * The class name of the reporter listener </span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line">  <span class="keyword">private</span> String m_className;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">/**</span></span><br><span class="line"><span class="comment">  * The properties of the reporter listener </span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line">  <span class="keyword">private</span> List&lt;Property&gt; m_properties = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Property&gt;();</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addProperty</span><span class="params">(Property property)</span> { </span><br><span class="line">    m_properties.add(property);</span><br><span class="line">  }</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>주석 제거로 한 눈에 들어오게 된 클래스</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReporterConfig</span> {</span><br><span class="line">  <span class="keyword">private</span> String m_className;</span><br><span class="line">  <span class="keyword">private</span> List&lt;Property&gt; m_properties = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;Property&gt;();</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addProperty</span><span class="params">(Property property)</span> { </span><br><span class="line">    m_properties.add(property);</span><br><span class="line">  }</span><br></pre></td></tr></tbody></table></figure><h4 id="수직-거리"><a href="#수직-거리" class="headerlink" title="수직 거리"></a>수직 거리</h4><p>이런 경험이 있는가?</p><ul><li>함수 연관 관계, 동작을 이해하려고 함수를 오가며 소스 파일을 위로 오가는데 못찾게 되는 경험?</li><li>함수/변수가 정의된 코드를 찾으려 상속 관계를 줄줄히 올라간 경험?</li><li>시스템을 이해하려는 목적과 달리 각 조각이 <strong>어디에</strong> 있는지 찾고 기억해야 함</li><li>시간과 노력을 엄청나게 소모</li><li><strong>서로 밀접한 개념은 세로로 가까이 둬야 한다</strong></li><li>두 개념이 서로 다른 파일에 속한다면 규칙이 통하지 않는다.</li><li>타당한 근거가 없다면 밀접한 개념은 한 파일에 → protected 변수를 피해야 하는 이유 중 하나</li><li>연관성 : 한 개념을 이해하는데 다른 개념이 중요한 정도</li><li>연관성 있는 개념이 떨어져있으면 읽는 사람이 파일/클래스를 뒤져야함</li></ul><h5 id="변수-선언"><a href="#변수-선언" class="headerlink" title="변수 선언"></a>변수 선언</h5><ul><li>변수는 사용 위치에 최대한 가까이 선언</li><li>좋은(작은)함수는 지역 변수 각 함수 맨 처음에 선언<figure class="highlight java"><figcaption><span>JUnit 4.3.1의 함수 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">readPreferences</span><span class="params">()</span> {</span><br><span class="line">  <span class="type">InputStream</span> <span class="variable">is</span> <span class="operator">=</span> <span class="literal">null</span>; <span class="comment">// 맨 처음에 선언</span></span><br><span class="line">  <span class="keyword">try</span> {</span><br><span class="line">    is = <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(getPreferencesFile()); </span><br><span class="line">    setPreferences(<span class="keyword">new</span> <span class="title class_">Properties</span>(getPreferences())); </span><br><span class="line">    getPreferences().load(is);</span><br><span class="line">  } <span class="keyword">catch</span> (IOException e) { </span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">      <span class="keyword">if</span> (is != <span class="literal">null</span>) </span><br><span class="line">        is.close();</span><br><span class="line">    } <span class="keyword">catch</span> (IOException e1) {</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>루프 제어 변수는 : 흔히 루프 안에 선언</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">countTestCases</span><span class="params">()</span> { </span><br><span class="line">  <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">for</span> (Test each : tests) <span class="comment">//이렇게~</span></span><br><span class="line">    count += each.countTestCases(); </span><br><span class="line">  <span class="keyword">return</span> count;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>긴 함수 : 블록 상단이나 루프 직전에 변수 선언 사례<br>(아주 드문 케이스)<figure class="highlight java"><figcaption><span>TestNG의 아주 긴 함수 중 일부</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (XmlTest test : m_suite.getTests()) {</span><br><span class="line">  <span class="type">TestRunner</span> <span class="variable">tr</span> <span class="operator">=</span> m_runnerFactory.newTestRunner(<span class="built_in">this</span>, test); <span class="comment">//이렇게 </span></span><br><span class="line">  tr.addListener(m_textReporter); </span><br><span class="line">  m_testRunners.add(tr);</span><br><span class="line"></span><br><span class="line">  invoker = tr.getInvoker();</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">for</span> (ITestNGMethod m : tr.getBeforeSuiteMethods()) { </span><br><span class="line">    beforeSuiteMethods.put(m.getMethod(), m);</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (ITestNGMethod m : tr.getAfterSuiteMethods()) { </span><br><span class="line">    afterSuiteMethods.put(m.getMethod(), m);</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h5 id="인스턴스-변수"><a href="#인스턴스-변수" class="headerlink" title="인스턴스 변수"></a>인스턴스 변수</h5><ul><li>자바 :클래스 맨 처음에 선언</li><li>C++ : scissors rule(모든 인스턴스 변수를 클래스 마지막에 선언한다)</li><li>중요한 것은 인스턴스 변수를 모으고 어디서 찾을 수 있는지 모두가 알고 있어야 함</li><li>변수간 세로 거리 X → 정상적인 클래스는 대다수의 클래스 메서드가 인스턴스 변수 사용<figure class="highlight java"><figcaption><span>JUnit4.3.1의 TestSuite 클래스 일부분</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestSuite</span> <span class="keyword">implements</span> <span class="title class_">Test</span> {</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">public</span> Test <span class="title function_">createTest</span><span class="params">(Class&lt;? extends TestCase&gt; theClass,</span></span><br><span class="line"><span class="params">                  String name)</span> {</span><br><span class="line">    ... </span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> Constructor&lt;? <span class="keyword">extends</span> <span class="title class_">TestCase</span>&gt; </span><br><span class="line">  getTestConstructor(Class&lt;? <span class="keyword">extends</span> <span class="title class_">TestCase</span>&gt; theClass) </span><br><span class="line">  <span class="keyword">throws</span> NoSuchMethodException {</span><br><span class="line">    ... </span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> Test <span class="title function_">warning</span><span class="params">(<span class="keyword">final</span> String message)</span> { </span><br><span class="line">    ...</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> String <span class="title function_">exceptionToString</span><span class="params">(Throwable t)</span> { </span><br><span class="line">    ...</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> String fName; <span class="comment">//이거 </span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> Vector&lt;Test&gt; fTests= <span class="keyword">new</span> <span class="title class_">Vector</span>&lt;Test&gt;(<span class="number">10</span>); <span class="comment">//이것도</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">TestSuite</span><span class="params">()</span> { }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">TestSuite</span><span class="params">(<span class="keyword">final</span> Class&lt;? extends TestCase&gt; theClass)</span> { </span><br><span class="line">    ...</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">TestSuite</span><span class="params">(Class&lt;? extends TestCase&gt; theClass, String name)</span> { </span><br><span class="line">    ...</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  ... ... ... ... ...</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h5 id="종속-함수-한-함수가-다른-함수-호출"><a href="#종속-함수-한-함수가-다른-함수-호출" class="headerlink" title="종속 함수 : 한 함수가 다른 함수 호출"></a>종속 함수 : 한 함수가 다른 함수 호출</h5><ul><li>세로로 가까이 배치 - 연관성끼리</li><li>가능하다면 호출하는 함수를 호출되는 함수보다 먼저 배치 - 자연스럽게 읽히도록</li><li>일관적으로 규칙 적용 한다면 → 읽는 사람은 호출된 함수가 잠시 뒤에 정의되리라고 예측</li><li>호출되는 함수 찾기가 쉬어지며 모듈 전체의 가독성도 올라간ㄷ<figure class="highlight java"><figcaption><span>FitNesse의 일부 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 가장 먼저 호출하는 함수가 바로 아래 정의</span></span><br><span class="line"><span class="comment">// 그다음 호출하는 함수는 그 다음 정의</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WikiPageResponder</span> <span class="keyword">implements</span> <span class="title class_">SecureResponder</span> { </span><br><span class="line">  <span class="keyword">protected</span> WikiPage page;</span><br><span class="line">  <span class="keyword">protected</span> PageData pageData;</span><br><span class="line">  <span class="keyword">protected</span> String pageTitle;</span><br><span class="line">  <span class="keyword">protected</span> Request request; </span><br><span class="line">  <span class="keyword">protected</span> PageCrawler crawler;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> Response <span class="title function_">makeResponse</span><span class="params">(FitNesseContext context, Request request)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="type">String</span> <span class="variable">pageName</span> <span class="operator">=</span> getPageNameOrDefault(request, <span class="string">"FrontPage"</span>);  </span><br><span class="line">    loadPage(pageName, context); </span><br><span class="line">    <span class="keyword">if</span> (page == <span class="literal">null</span>)</span><br><span class="line">      <span class="keyword">return</span> notFoundResponse(context, request); </span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">return</span> makePageResponse(context); </span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">getPageNameOrDefault</span><span class="params">(Request request, String defaultPageName)</span> {</span><br><span class="line">    <span class="type">String</span> <span class="variable">pageName</span> <span class="operator">=</span> request.getResource(); </span><br><span class="line">    <span class="keyword">if</span> (StringUtil.isBlank(pageName))</span><br><span class="line">      pageName = defaultPageName;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> pageName; </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">loadPage</span><span class="params">(String resource, FitNesseContext context)</span></span><br><span class="line">    <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="type">WikiPagePath</span> <span class="variable">path</span> <span class="operator">=</span> PathParser.parse(resource);</span><br><span class="line">    crawler = context.root.getPageCrawler();</span><br><span class="line">    crawler.setDeadEndStrategy(<span class="keyword">new</span> <span class="title class_">VirtualEnabledPageCrawler</span>()); </span><br><span class="line">    page = crawler.getPage(context.root, path);</span><br><span class="line">    <span class="keyword">if</span> (page != <span class="literal">null</span>)</span><br><span class="line">      pageData = page.getData();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> Response <span class="title function_">notFoundResponse</span><span class="params">(FitNesseContext context, Request request)</span></span><br><span class="line">    <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">NotFoundResponder</span>().makeResponse(context, request);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> SimpleResponse <span class="title function_">makePageResponse</span><span class="params">(FitNesseContext context)</span></span><br><span class="line">    <span class="keyword">throws</span> Exception {</span><br><span class="line">    pageTitle = PathParser.render(crawler.getFullPath(page)); </span><br><span class="line">    <span class="type">String</span> <span class="variable">html</span> <span class="operator">=</span> makeHtml(context);</span><br><span class="line">    <span class="type">SimpleResponse</span> <span class="variable">response</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleResponse</span>(); </span><br><span class="line">    response.setMaxAge(<span class="number">0</span>); </span><br><span class="line">    response.setContent(html);</span><br><span class="line">    <span class="keyword">return</span> response;</span><br><span class="line">  } </span><br><span class="line">    <span class="comment">/*makeResponse 함수에서 호출하는 getPageNameOrDefault함수 안에서 "FrontPage" 상수를 사용하지 않고,</span></span><br><span class="line"><span class="comment">상수를 알아야 의미 전달이 쉬워지는 함수 위치에서 실제 사용하는 함수로 상수를 넘겨주는 방법이</span></span><br><span class="line"><span class="comment">가독성 관점에서 훨씬 더 좋다*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">그렇다면 getPageNameOrDefault 함수안에서 isBlank 판단 이후 상수를 사용하는게 좋아 보일 듯</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">...</span><br></pre></td></tr></tbody></table></figure></li><li>위 코드는 상수를 적절한 수준에 두는 좋은 예제<ul><li>예)<ul><li>makeResponse 함수에서 호출하는 getPageNameOrDefault함수 안에서 “FrontPage” 상수를 사용하지 않고</li><li>getPageNameOrDefault 함수안에서 isBlank 판단 이후 상수를 사용</li></ul></li><li>위의 예에는 : 기대와 달리 잘 알려진 상수가 적절하지 않은 저차원 함수에 묻힌다</li><li>[G35-p394]: <strong>구성 정보는 최상위 단계에 두어라 : 상수를 저차원 함수에 숨기지 말고 고차원에 두어야 저차원 함수를 뒤적이지 않게 됨</strong></li><li>상수를 알아야 마땅한 함수에서 실제 사용하는 함수로 상수를 넘겨주는 방법이 더 좋다</li></ul></li></ul><h3 id="개념적-유사성"><a href="#개념적-유사성" class="headerlink" title="개념적 유사성"></a>개념적 유사성</h3><ul><li>개념적인 친화도가 높은 코드는 서로를 끌어당김 → 친화도가 높을 수록 가까이 배치</li><li>친화도 요인 : 종속성, 변수 사용, 비슷한 동작을 수행하는 일군의 함수</li></ul><figure class="highlight java"><figcaption><span>Junit4.3.1 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Assert</span> {</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">assertTrue</span><span class="params">(String message, <span class="type">boolean</span> condition)</span> {</span><br><span class="line">    <span class="keyword">if</span> (!condition) </span><br><span class="line">      fail(message);</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">assertTrue</span><span class="params">(<span class="type">boolean</span> condition)</span> { </span><br><span class="line">    assertTrue(<span class="literal">null</span>, condition);</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">assertFalse</span><span class="params">(String message, <span class="type">boolean</span> condition)</span> { </span><br><span class="line">    assertTrue(message, !condition);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">assertFalse</span><span class="params">(<span class="type">boolean</span> condition)</span> { </span><br><span class="line">    assertFalse(<span class="literal">null</span>, condition);</span><br><span class="line">  } </span><br><span class="line">...</span><br></pre></td></tr></tbody></table></figure><ul><li>개념적 친화도가 매우 높은 함수들임<ul><li>명명법이 똑깥고 기본 기능이 유사하고 간단</li><li>서로가 서로를 호출하는 것은 부차적 요인 → 종속 관계가 없어도 가까이 배치할 함수</li></ul></li></ul><h4 id="세로-순서"><a href="#세로-순서" class="headerlink" title="세로 순서"></a>세로 순서</h4><ul><li><p>함수 호출 종속성 아래 방향 유지 → 자연스럽게 모듈이 고차원→저차원으로 자연스럽게 내려감</p><ul><li>호출되는 함수를 호출하는 함수보다 나중에 배치</li><li>C, C++, Pascal과 정확히 반대 : 함수 호출을 위해 적어도 미리 선언은 해놔야하는 언어</li></ul></li><li><p>신문 기사와 같이</p><ul><li>가장 중요한 개념 가장 먼저 표현 : 세세한 사항을 최대한 배제</li><li>세세한 사항은 가장 마지막에 표현</li><li>독자들이 첫 함수 몇 개만 읽어도 개념 파악이 되도록</li><li>좋은 목록 3-7(ch3마지막),  15-5(p338, )</li></ul></li></ul><h3 id="가로-형식-맞추기"><a href="#가로-형식-맞추기" class="headerlink" title="가로 형식 맞추기"></a>가로 형식 맞추기</h3><ul><li><p>과거 훌러리스(Hollerith) :80자 제한 </p><ul><li>가로 80, 세로 12단 천공카드, 기계를 고안한 통계학자</li></ul></li><li><p>가급적 짧은 행이 바람직함</p></li><li><p>120자 정도로 행 길이 제한 권고 - 인텔리 제이 120으로 되어있음</p></li></ul><h4 id="가로-공백과-밀집도"><a href="#가로-공백과-밀집도" class="headerlink" title="가로 공백과 밀집도"></a>가로 공백과 밀집도</h4><p>가로 공백 : 밀접한 개념, 느슨한 개념 표현 가능</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">measureLine</span><span class="params">(String line)</span> { </span><br><span class="line">  lineCount++;</span><br><span class="line">  <span class="type">int</span> <span class="variable">lineSize</span> <span class="operator">=</span> line.length(); <span class="comment">// 할당 연산자 좌우로 공백 → 좌우 요소가 확실하게 구분.</span></span><br><span class="line">  totalChars += lineSize; </span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 괄호 안의 인수끼리는 쉼표 뒤의 공백을 통해 인수가 별개라는 사실을 보여준다.</span></span><br><span class="line">    <span class="comment">// 함수명과 괄호는 공백없음 - 함수와 인수의 밀접함</span></span><br><span class="line">    <span class="comment">// 괄호 안의 인자끼리는 쉼표 뒤의 공백 → 쉼표 강조 → 각 인자가 별개라는 의미</span></span><br><span class="line">  lineWidthHistogram.addLine(lineSize, lineCount);</span><br><span class="line">  recordWidestLine(lineSize);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>연산자의 우선순위를 강조하기 위해서도 공백을 사용</p><ul><li>불행히 IDE의 포매터가 나중에 해당 공백을 삭제하는 경우가 흔함</li><li>포매터 대부분 연산자 우선순위 고려 불가하므로 수식에 같은 간격 적용하기 때문</li></ul><h4 id="가로-정렬"><a href="#가로-정렬" class="headerlink" title="가로 정렬"></a>가로 정렬</h4><ul><li>밥아저씨의 정렬<ul><li>어셈블리어 영향</li></ul></li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FitNesseExpediter</span> <span class="keyword">implements</span> <span class="title class_">ResponseSender</span> {</span><br><span class="line">  <span class="keyword">private</span>Socket        socket;</span><br><span class="line">  <span class="keyword">private</span> InputStream         input;</span><br><span class="line">  <span class="keyword">private</span> OutputStream        output;</span><br><span class="line">  <span class="keyword">private</span> Reques        request; </span><br><span class="line">  <span class="keyword">private</span> Response         response;</span><br><span class="line">  <span class="keyword">private</span> FitNesseContex      context; </span><br><span class="line">  <span class="keyword">protected</span> <span class="type">long</span>        requestParsingTimeLimit;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">long</span>        requestProgress;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">long</span>        requestParsingDeadline;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span>        hasError;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">FitNesseExpediter</span><span class="params">(Socket          s,</span></span><br><span class="line"><span class="params">                             FitNesseContext context)</span> <span class="keyword">throws</span> Exceptions</span><br><span class="line">    {</span><br><span class="line">        <span class="built_in">this</span>.context =              context;</span><br><span class="line">        socket =                    s;</span><br><span class="line">        input =                     s.getInputStream();</span><br><span class="line">        output =                    s.getOutputStream();</span><br><span class="line">        requestParsingTimeLimit =   <span class="number">10000</span>;</span><br><span class="line">    }</span><br></pre></td></tr></tbody></table></figure><p>좋지 않은 정렬</p><ul><li>보기엔 깔끔해 보여도 코드의 엉뚱한 부분이 강조되 진짜 의도가 가려지게 됨</li><li>변수유형대신 변수 이름부터 읽게 됨</li><li>할당문 : 할당 연산자가 눈에 안보이고 우측 피연산자만 눈에 보임</li><li>대다수의 포매터는 위의 정렬을 무시</li></ul><p>선언문/할당문 별도 정렬 필요 X</p><ul><li>정렬이 필요할 정도로 선언부가 길다면 클래스를 쪼개야 한다</li></ul><h4 id="들여쓰기"><a href="#들여쓰기" class="headerlink" title="들여쓰기"></a>들여쓰기</h4><ul><li>Scope를 한눈에 표현 : 프로그래머들이 크게 의존</li></ul><h5 id="들여쓰기-무시하기"><a href="#들여쓰기-무시하기" class="headerlink" title="들여쓰기 무시하기"></a>들여쓰기 무시하기</h5><ul><li>들여쓰기를 무식하고 싶은 유혹들 : 간단한 if, while, 짧은 함수</li></ul><figure class="highlight java"><figcaption><span>들여쓰기 안하고 한 행에 범위를 뭉뚱그린 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommentWidget</span> <span class="keyword">extends</span> <span class="title class_">TextWidget</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">REGEXP</span> <span class="operator">=</span> <span class="string">"^#[^\r\n]*(?:(?:\r\n)|\n|\r)?"</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">CommentWidget</span><span class="params">(ParentWidget parent, String text)</span>{<span class="built_in">super</span>(parent, text);}</span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">render</span><span class="params">()</span> <span class="keyword">throws</span> Exception {<span class="keyword">return</span> <span class="string">""</span>; }  <span class="comment">//멋져보이나 아니야</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>한 줄 함수라도 들여쓰기를 하자</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CommentWidget</span> <span class="keyword">extends</span> <span class="title class_">TextWidget</span> {</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">REGEXP</span> <span class="operator">=</span> <span class="string">"^#[^\r\n]*(?:(?:\r\n)|\n|\r)?"</span>;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">CommentWidget</span><span class="params">(ParentWidget parent, String text)</span>{</span><br><span class="line">    <span class="built_in">super</span>(parent, text);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> String <span class="title function_">render</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">return</span> <span class="string">""</span>; </span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="가짜-범위"><a href="#가짜-범위" class="headerlink" title="가짜 범위"></a>가짜 범위</h4><ul><li>가능한 피하라</li><li>피하지 못할 땐 밑에처럼<figure class="highlight java"><figcaption><span>빈 while 문을 어쩔수 없이 쓸때는 괄호로 감싸고 새로운 행에 세미콜론</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> (dis.read(buf, <span class="number">0</span>, readBufferSize) != -<span class="number">1</span>)</span><br><span class="line">;</span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="팀-규칙"><a href="#팀-규칙" class="headerlink" title="팀 규칙"></a>팀 규칙</h3><ul><li>팀에 속해있다면 팀 규칙이 가장 우선시 되고 선호되어야 한다</li><li>팀은 한가지 규칙에 합의 필요<ul><li>sw 일관적인 코딩스타일 적용 필요</li></ul></li><li>좋은 소프트웨어 시스템은 읽기 쉬운 문서로 이뤄지고, 읽기 쉬운 문서는 스타일이 일관적이고 매끄러워야 한다.</li></ul><h3 id="밥-아저씨의-형식-규칙"><a href="#밥-아저씨의-형식-규칙" class="headerlink" title="밥 아저씨의 형식 규칙"></a>밥 아저씨의 형식 규칙</h3><ul><li>이러한 규칙의 예제</li></ul><figure class="highlight java"><figcaption><span>코드 자체가 구현 문서가 되는 예제</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CodeAnalyzer</span> <span class="keyword">implements</span> <span class="title class_">JavaFileAnalysis</span> { </span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> lineCount;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> maxLineWidth;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> widestLineNumber;</span><br><span class="line">  <span class="keyword">private</span> LineWidthHistogram lineWidthHistogram; </span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> totalChars;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="title function_">CodeAnalyzer</span><span class="params">()</span> {</span><br><span class="line">    lineWidthHistogram = <span class="keyword">new</span> <span class="title class_">LineWidthHistogram</span>();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> List&lt;File&gt; <span class="title function_">findJavaFiles</span><span class="params">(File parentDirectory)</span> { </span><br><span class="line">    List&lt;File&gt; files = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;File&gt;(); </span><br><span class="line">    findJavaFiles(parentDirectory, files);</span><br><span class="line">    <span class="keyword">return</span> files;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">findJavaFiles</span><span class="params">(File parentDirectory, List&lt;File&gt; files)</span> {</span><br><span class="line">    <span class="keyword">for</span> (File file : parentDirectory.listFiles()) {</span><br><span class="line">      <span class="keyword">if</span> (file.getName().endsWith(<span class="string">".java"</span>)) </span><br><span class="line">        files.add(file);</span><br><span class="line">      <span class="keyword">else</span> <span class="keyword">if</span> (file.isDirectory()) </span><br><span class="line">        findJavaFiles(file, files);</span><br><span class="line">    } </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">analyzeFile</span><span class="params">(File javaFile)</span> <span class="keyword">throws</span> Exception { </span><br><span class="line">    <span class="type">BufferedReader</span> <span class="variable">br</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">FileReader</span>(javaFile)); </span><br><span class="line">    String line;</span><br><span class="line">    <span class="keyword">while</span> ((line = br.readLine()) != <span class="literal">null</span>)</span><br><span class="line">      measureLine(line); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">measureLine</span><span class="params">(String line)</span> { </span><br><span class="line">    lineCount++;</span><br><span class="line">    <span class="type">int</span> <span class="variable">lineSize</span> <span class="operator">=</span> line.length();</span><br><span class="line">    totalChars += lineSize; </span><br><span class="line">    lineWidthHistogram.addLine(lineSize, lineCount);</span><br><span class="line">    recordWidestLine(lineSize);</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">recordWidestLine</span><span class="params">(<span class="type">int</span> lineSize)</span> { </span><br><span class="line">    <span class="keyword">if</span> (lineSize &gt; maxLineWidth) {</span><br><span class="line">      maxLineWidth = lineSize;</span><br><span class="line">      widestLineNumber = lineCount; </span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getLineCount</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> lineCount;</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMaxLineWidth</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> maxLineWidth;</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getWidestLineNumber</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> widestLineNumber;</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> LineWidthHistogram <span class="title function_">getLineWidthHistogram</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">return</span> lineWidthHistogram;</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">getMeanLineWidth</span><span class="params">()</span> { </span><br><span class="line">    <span class="keyword">return</span> (<span class="type">double</span>)totalChars/lineCount;</span><br><span class="line">  }</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMedianLineWidth</span><span class="params">()</span> {</span><br><span class="line">    Integer[] sortedWidths = getSortedWidths(); </span><br><span class="line">    <span class="type">int</span> <span class="variable">cumulativeLineCount</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> width : sortedWidths) {</span><br><span class="line">      cumulativeLineCount += lineCountForWidth(width); </span><br><span class="line">      <span class="keyword">if</span> (cumulativeLineCount &gt; lineCount/<span class="number">2</span>)</span><br><span class="line">        <span class="keyword">return</span> width;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">"Cannot get here"</span>); </span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">lineCountForWidth</span><span class="params">(<span class="type">int</span> width)</span> {</span><br><span class="line">    <span class="keyword">return</span> lineWidthHistogram.getLinesforWidth(width).size();</span><br><span class="line">  }</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> Integer[] getSortedWidths() {</span><br><span class="line">    Set&lt;Integer&gt; widths = lineWidthHistogram.getWidths(); </span><br><span class="line">    Integer[] sortedWidths = (widths.toArray(<span class="keyword">new</span> <span class="title class_">Integer</span>[<span class="number">0</span>])); </span><br><span class="line">    Arrays.sort(sortedWidths);</span><br><span class="line">    <span class="keyword">return</span> sortedWidths;</span><br><span class="line">  } </span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="정리"><a href="#정리" class="headerlink" title="정리"></a>정리</h2><ol><li>포맷팅이 중요한 이유</li></ol><ul><li>가독성에 필수적</li><li>아마추어적으로 보이지 않는다</li><li>잘못된 포맷팅으로 코드를 잘못 이해해서 버그를 발생할 위험을 줄일 수 있음</li><li>어렸을때 띄어쓰기, 문법 공부 - &gt; 이게 잘 되어 있으면 글이 잘 읽힌다</li><li>기본 중의 기본</li></ul><ol start="2"><li>클린 코드 포매팅</li></ol><ul><li>적절한 길이<ul><li>200라인 이하 권장 , 500라인 이하 권장</li><li>현업에서 실제 대부분의 코드도 200라인 유지</li><li>200라인이 넘어가면 클래스가 여러가지 일을 수행할 확율이 높은 → SRP 위배</li></ul></li><li>밀접 개념 가까이<ul><li>행 묶음은 완결된 생각 하나 표현함 → 개념은 빈 행으로 분리</li><li>변수는 사용되는 위치에서 최대한 가까이 선언</li></ul></li></ul><ol start="3"><li>Java Class Declarations</li></ol><ul><li>오라클 : <a href="https://www.oracle.com/java/technologies/javase/codeconventions-fileorganization.html#1852">https://www.oracle.com/java/technologies/javase/codeconventions-fileorganization.html#1852</a><ul><li>static variable → instance variables → Constructors → Methods</li><li>변수는 : public → protected → package → private 순서</li><li>메서드<ul><li>스코프와 접근에 따라 그루핑이 되어야 한</li><li>public 메서드에서 private 메서드는 그 아래에 둔다 → 그래야 가독성 높아짐</li><li>가독성 위주로 그룹핑</li></ul></li></ul></li></ul><ol start="4"><li>Team Coding Convention</li></ol><ul><li>코딩 스타일에 관한 약속</li><li>개발 언어의 컨벤션이 우선, 애매하면(변수명) 팀 컴벤션을 따른다<ul><li>예) enum타입으로 사용하는 varchar타입의 경우 컬럼명은 _type로 끝나도록 네이밍한다</li></ul></li><li>없다면 함께 만들어가는게 좋다</li><li>참고할 만한 컨벤션<ul><li>mysql 컨벤션 : snake_case</li><li>Google Java Style Guide: <a href="https://google.github.io/styleguide/javaguide.html">https://google.github.io/styleguide/javaguide.html</a><ul><li>현업에서 많이 참고</li></ul></li><li>네이버 헥데이 코딩 컨벤션 : <a href="https://naver.github.io/hackday-conventions-java">https://naver.github.io/hackday-conventions-java</a><ul><li>네이버에서 헥데이라는 행사에서 쓰기 위한 컨벤션</li><li>네이버 현업과 비슷하고 한글로 되어 있음</li></ul></li></ul></li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch5/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅳ. 주석</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch4/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch4/</guid>
      <pubDate>Wed, 09 Feb 2022 00:52:21 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅳ-주석&quot;&gt;&lt;a href=&quot;#Ⅳ-주석&quot; class=&quot;headerlink&quot; title=&quot;Ⅳ. 주석&quot;&gt;&lt;/a&gt;Ⅳ. 주석&lt;/h1&gt;&lt;b</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅳ-주석"><a href="#Ⅳ-주석" class="headerlink" title="Ⅳ. 주석"></a>Ⅳ. 주석</h1><blockquote><p>나쁜 코드에 주석을 달지 말아라. 새로 짜라.</p><footer><strong>브라이언 W. 커니핸 P.J 플라우거</strong></footer></blockquote><p>주석</p><ul><li>잘달린 주석 : 그 어떤 정보보다 유용</li><li>경솔하고 근거 없는 주석 : 코드 이해 어렵게 만듬</li><li>오래되고 조잡한 주석 : 거짓과 잘못된 정보를 퍼뜨려 악영향</li></ul><p>주석은 필요악</p><ul><li>주석은 순수하게 선하지 않다</li><li>프로그래밍 언어 자체가 표현력이 풍부하다면</li><li>아니면 개발자(우리자신)가 프로그래밍 언어를 사용해 의도를 표현할 능력이 있다면</li><li>주석은 전혀 필요하지 않을 것이다</li><li>주석은 실패를 의미하며 줄이려 노력해야 한다<ul><li>주석이 없이는 자신을 표현을 할 수 없기에 사용</li><li>주석의 내용을 코드로 의도를 표현하는 것이 가장 좋다</li><li>코드는 변화하고 진화하며 일부가 옮겨지고 조각이 나뉘거나 병합된다</li><li>주석은 코드를 따라가지 못한다 : 주석을 유지보수 하는 것은 불가능</li><li>코드만이 정확한 정보를 제공한다</li><li><strong>부정확한 주석은 없는 주석보다 나쁘다</strong></li></ul></li></ul><h2 id="주석은-나쁜-코드를-보완하지-않는다"><a href="#주석은-나쁜-코드를-보완하지-않는다" class="headerlink" title="주석은 나쁜 코드를 보완하지 않는다"></a>주석은 나쁜 코드를 보완하지 않는다</h2><p>주석이 필요한 이유? 나쁜 코드</p><ul><li>모듈을 작성하니 엉망이고 알어먹기 어려움</li><li>그래서 주석을 달아야겠다? → No! 코드를 정리해야 한다</li><li>표현력이 풍부, 깔끔하고 주석이 없는 코드 &gt; 복잡하고 어수선하나 주석이 아주 많이 달려있는 코드</li><li>어지름을 주석으로 설명하는 노력으로 그 어지름을 치우는 데에 시간과 노력을 투자하라</li></ul><h2 id="코드로-의도를-표현하라"><a href="#코드로-의도를-표현하라" class="headerlink" title="코드로 의도를 표현하라"></a>코드로 의도를 표현하라</h2><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 어떤 쪽이 좋을까</span></span><br><span class="line"><span class="comment">//////////////////</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1.</span></span><br><span class="line"><span class="comment">// 지구언에게 복지 혜택을 받을 자격이 있는지 검사한다.</span></span><br><span class="line"><span class="keyword">if</span>((employee.flags &amp; HOURLY_FLAG) &amp;&amp; (employee.age &gt; <span class="number">65</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2.</span></span><br><span class="line"><span class="keyword">if</span> (employee.isEligibleForFullBenefits())</span><br></pre></td></tr></tbody></table></figure><ul><li>몇 초만 더 생각하면 코드로 대다수 의도를 표현 가능</li><li>많은 경우 주석으로 달려는 설명을 함수로 만들어 표현해도 충분</li></ul><h2 id="좋은-주석"><a href="#좋은-주석" class="headerlink" title="좋은 주석"></a>좋은 주석</h2><p>글자값을 한다고 생각되는 주석</p><h3 id="법적인-주석"><a href="#법적인-주석" class="headerlink" title="법적인 주석"></a>법적인 주석</h3><ul><li>회사가 정립한 구현 표준에 맞춰 법적인 이유로 넣는 주석</li><li>ex)각 소스 파일 첫머리에 주석으로 들어가는 저작권/소유권 정보 : 필요하고도 타당</li><li>가능하다면, 표준 라이선스나 외부 파일을 참조<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Copyright (C) 2003, 2004, 2005 by Object Montor, Inc. All right reserved.</span></span><br><span class="line"><span class="comment">// GNU General Public License</span></span><br><span class="line">```  </span><br><span class="line">### 정보를 제공하는 주석</span><br><span class="line">  - 예<span class="number">1</span>)기본정보를 주석으로 제공</span><br><span class="line">    - 유용함</span><br><span class="line">    - 가능하면 함수 이름에 정보를 담는 편이 더 좋음 - 네이밍의 중요성</span><br><span class="line">  - 예<span class="number">2</span>)정규표현식의 결과물 형태를 주석으로</span><br><span class="line">    - 시각과 날짜를 변환하는 클래스를 만들어 코드를 옮겨주면 주석이 필요가 없음</span><br><span class="line">```java</span><br><span class="line"><span class="comment">// 1.</span></span><br><span class="line"><span class="comment">// 테스트 중인 Responder 인스턴스를 반환</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">abstract</span> Responder <span class="title function_">responderInstance</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 위의 경우 함수이름에 정보를 담아 다음처럼 작성하면 주석을 없앨 수 있다</span></span><br><span class="line"><span class="keyword">protected</span> <span class="keyword">abstract</span> Responder <span class="title function_">responderBeingTested</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2.</span></span><br><span class="line"><span class="comment">// 좋은 주석</span></span><br><span class="line"><span class="comment">// kk:mm:ss EEE, MMM dd, yyyy 형식이다.</span></span><br><span class="line"><span class="type">Pattern</span> <span class="variable">timeMatcher</span> <span class="operator">=</span> Pattern.compile(<span class="string">"\\d*:\\d*\\d* \\w*, \\w*, \\d*, \\d*"</span>);</span><br><span class="line">```    </span><br><span class="line"></span><br><span class="line">### 의도를 설명하는 주석</span><br><span class="line">```java</span><br><span class="line"><span class="comment">// 스레드를 대량 생성하는 방법으로 어떻게든 경쟁 조건을 만들려 시도한다.</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &gt; <span class="number">2500</span>; i++) {</span><br><span class="line">    <span class="type">WidgetBuilderThread</span> <span class="variable">widgetBuilderThread</span> <span class="operator">=</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">WidgetBuilderThread</span>(widgetBuilder, text, parent, failFlag);</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(widgetBuilderThread);</span><br><span class="line">    thread.start();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>때로는 주석으로 구현 이해를 도와주는 걸 넘어서 결졍에 깔린 의도를 설명하기도 한다 <figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">compareTo</span><span class="params">(Object o)</span> {</span><br><span class="line">    <span class="keyword">if</span> (o <span class="keyword">instanceof</span> WikiPagePath) {</span><br><span class="line">        <span class="type">WikiPagePath</span> <span class="variable">p</span> <span class="operator">=</span> (WikiPagePath) o;</span><br><span class="line">        <span class="type">String</span> <span class="variable">compressName</span> <span class="operator">=</span> StringUtil.join(names, <span class="string">""</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">compressedArgumentName</span> <span class="operator">=</span> StringUtil.join(p.names, <span class="string">""</span>);</span><br><span class="line">        <span class="keyword">return</span> compressedName.compareTo(compressedArgumentName);</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">return</span> <span class="number">1</span>;  <span class="comment">// 오른쪽 유형이므로 정렬 순위가 더 높다.</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="의미를-명료하게-밝히는-주석"><a href="#의미를-명료하게-밝히는-주석" class="headerlink" title="의미를 명료하게 밝히는 주석"></a>의미를 명료하게 밝히는 주석</h3><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">testCompareTo</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="type">WikiPagePath</span> <span class="variable">a</span> <span class="operator">=</span> PathParser.parse(<span class="string">"PageA"</span>);</span><br><span class="line">    <span class="type">WikiPagePath</span> <span class="variable">ab</span> <span class="operator">=</span> PathParser.parse(<span class="string">"PageA.PageB"</span>);</span><br><span class="line">    <span class="type">WikiPagePath</span> <span class="variable">b</span> <span class="operator">=</span> PathParser.parse(<span class="string">"PageB"</span>);</span><br><span class="line">    </span><br><span class="line">    assertTrue(a.compareTo(a) == <span class="number">0</span>);    <span class="comment">// a == a</span></span><br><span class="line">    asserTrue(a.compareTo(b) != <span class="number">0</span>);     <span class="comment">// a != b</span></span><br><span class="line">    assertTrue(ab.compareTo(ab) == <span class="number">0</span>);  <span class="comment">// ab == ab</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>모호한 인수나 반환값은 그 자체를 명확하게 만드는게 더 좋다 - 이해가 쉬어지므로</li><li>인수/반환값이 표준라이브러리 이거나 변경하지 못하는 코드? 의미를 명료하게 밝히는 주석이 유용</li><li>그릇된 주석의 위험, 올바른지 검증이 안되는 위험 - 주석의 위험</li><li>더 나은 방법이 없는지 고민할 것</li></ul><h3 id="결과를-경고하는-주석"><a href="#결과를-경고하는-주석" class="headerlink" title="결과를 경고하는 주석"></a>결과를 경고하는 주석</h3><ul><li>다른 프로그래머에게 결과를 경고할 목적<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 여유 시간이 충분하지 않다면 실행하지 마십시오.</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">_testWithReallyBigFile</span><span class="params">()</span> {</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>ex)시간이 굉장히 오래걸리는 테스트 케이스</li><li>JUnit에선 어노테이션으로 간단하게 <code>@Ignore("실행이 너무 오래걸림")</code></li></ul><h3 id="TODO-주석"><a href="#TODO-주석" class="headerlink" title="TODO 주석"></a>TODO 주석</h3><ul><li><code>//TODO 주석</code><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// TODO-MdM 현재 필요하지 않다.</span></span><br><span class="line"><span class="comment">// 체크아웃 모델을 도입하면 함수가 필요 없다.</span></span><br><span class="line"><span class="keyword">protected</span> VersionInfo <span class="title function_">makeVersion</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>앞으로 할일, 현재 이유, 미래 모습등</li><li>필요하지만 당장 구현하기 어려운 업무를 기술</li><li>ex)더 좋은 이름을 떠올려달라, 문제를 봐달라, 더이상 필요없는 기능을 삭제해라, 이벤트에 맞춰 코드를 고쳐라</li><li>요새 IDE는 TODO주석을 찾아줌 : 주석을 잊을 위험은 없음, 그래도 한번씩 점검해서 없어도 괜찮은 주석은 삭제</li><li>FIXME : 문제가 있지만 당장 수정할 필요는 없을 때(가능하면 빨리 수정하는 것이 좋음)</li></ul><h3 id="중요성-강조-주석"><a href="#중요성-강조-주석" class="headerlink" title="중요성 강조 주석"></a>중요성 강조 주석</h3><h3 id="공개-API에서의-Javadocs"><a href="#공개-API에서의-Javadocs" class="headerlink" title="공개 API에서의 Javadocs"></a>공개 API에서의 Javadocs</h3><p>설명이 잘된 공개 API는 참으로 유용하고 만족스럽다</p><ul><li>ex) 표준 자바 라이브러리에서 사용한 Javadocs가 좋은 예</li><li>공개 API를 구현한다면 반드시 훌륭한 Javadocs를 작성하라</li><li>어느 주석과 마찬가지로 Javadocs도 잘못될 가능성이 존재</li></ul><p><strong>정말 좋은 주석 - 주석을 달지 않을 방법을 찾아낸 주석!</strong></p><h2 id="나쁜-주석-x3D-대부분의-주석-대다수의-주석"><a href="#나쁜-주석-x3D-대부분의-주석-대다수의-주석" class="headerlink" title="나쁜 주석 = 대부분의 주석, 대다수의 주석"></a>나쁜 주석 = 대부분의 주석, 대다수의 주석</h2><ul><li>많은 주석 : 허술한 코드 지탱, 엉성한 코드 변명, 미숙한 결정 합리화</li></ul><h3 id="주절거리는-주석"><a href="#주절거리는-주석" class="headerlink" title="주절거리는 주석"></a>주절거리는 주석</h3><ul><li>특별한 이유 없이 다는 주석 → 시간 낭비</li><li>주석을 막상 단다면 충분히 시간을 들여 최고의 주석을 달도록 노력</li><li>이해가 안되어 다른 모듈을 뒤져야하는 주석 → 독자와 소통하지 못하는 주석 → 바이트 낭비</li><li>아래가 바이트 낭비의 예  :  주석의 의미를 알려면 다른 모듈을 뒤져야 함<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">loadProperties</span><span class="params">()</span> {</span><br><span class="line">    <span class="keyword">try</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">propertiesPath</span> <span class="operator">=</span> propertiesLocation + <span class="string">"/"</span> + PROPERTIES_FILE;</span><br><span class="line">        <span class="type">FileInputStream</span> <span class="variable">propertiesStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(propertiesPath);</span><br><span class="line">        loadedProperties.load(propertiesStream);</span><br><span class="line">    } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line">        <span class="comment">// 속성 파일이 없다면 기본값을 모두 메모리로 읽어 들였다는 의미다.</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="같은-이야기-중복하는-주석"><a href="#같은-이야기-중복하는-주석" class="headerlink" title="같은 이야기 중복하는 주석"></a>같은 이야기 중복하는 주석</h3><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// this.closed가 true일 때 반환되는 유틸리티 메서드다.</span></span><br><span class="line"><span class="comment">// 타임아웃에 도달하면 예외를 던진다.</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">waitForClose</span><span class="params">(<span class="keyword">final</span> <span class="type">long</span> timeoutMillis)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">if</span> (!closed) {</span><br><span class="line">        wait(timeoutMillis);</span><br><span class="line">        <span class="keyword">if</span> (!closed) {</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">"MockResponseSender could not be closed"</span>);</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>→ 코드 내용이 그대로 설명하는 주석</p><ul><li>주석이 코드보다 더 많은 정보를 제공하지 못함</li><li>의도/근거를 설명하는 주석도 아니고 코드보다 읽기가 쉽지도 않음</li><li>코드보다 부정확 → 독자가 함수를 대충 이해하고 넘어가게 만듬</li></ul><h3 id="오해-여지가-있는-주석"><a href="#오해-여지가-있는-주석" class="headerlink" title="오해 여지가 있는 주석"></a>오해 여지가 있는 주석</h3><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// this.closed가 true일 때 반환되는 유틸리티 메서드다.</span></span><br><span class="line"><span class="comment">// 타임아웃에 도달하면 예외를 던진다.</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">waitForClose</span><span class="params">(<span class="keyword">final</span> <span class="type">long</span> timeoutMillis)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">if</span> (!closed) {</span><br><span class="line">        wait(timeoutMillis);</span><br><span class="line">        <span class="keyword">if</span> (!closed)</span><br><span class="line">            <span class="keyword">throws</span> <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">"MockResponseSend could not be close"</span>);</span><br><span class="line">    }</span><br><span class="line">}<span class="number">0</span></span><br></pre></td></tr></tbody></table></figure><ul><li><code>this.closed</code>가 true로 되는 순간에 메서드 반환이 아닌 true여야 반환되는 것이다</li><li>주석만 본 프로그래머는 왜 코드가 느려지는지 알 수가 없음</li></ul><h3 id="의무적으로-다는-주석"><a href="#의무적으로-다는-주석" class="headerlink" title="의무적으로 다는 주석"></a>의무적으로 다는 주석</h3><ul><li>모든 함수에 Javadocs를 달거나 모든 필드에 주석 : 어리석기 그지 없음</li><li>코드를 복잡하게 만들고 거짓말을 퍼뜨리고 혼동과 무질서를 초래하는 아무 가치가 없는 주석<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> title CD 제목</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> author CD 저자</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> tracks CD 트랙 숫자</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> durationInMinutes CD 길이(단위: 분)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addCD</span><span class="params">(String title, String author, <span class="type">int</span> tracks, <span class="type">int</span> durationInMinutes)</span> {</span><br><span class="line">    <span class="type">CD</span> <span class="variable">cd</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CD</span>();</span><br><span class="line">    cd.title = title;</span><br><span class="line">    cd.author = author;</span><br><span class="line">    cd.tracks = tracks;</span><br><span class="line">    cd.duration = durationInMinutes;</span><br><span class="line">    cdList.add(cd);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h3 id="이력을-기록하는-주석"><a href="#이력을-기록하는-주석" class="headerlink" title="이력을 기록하는 주석"></a>이력을 기록하는 주석</h3><ul><li>소스 코드 관리 시스템이 없었던 시절의 바람직한 관례</li><li>현재는 혼란만 가중하므로 완전히 제거할것</li></ul><h3 id="있으나-마나-하는-주석"><a href="#있으나-마나-하는-주석" class="headerlink" title="있으나 마나 하는 주석"></a>있으나 마나 하는 주석</h3><p>→ 너무 당연한 사실을 언급하고 새로운 정보를 제공못하는 주석</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 기본 생성자</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">protected</span> <span class="title function_">AnnualDateRule</span><span class="params">()</span> {</span><br><span class="line">    <span class="comment">/** 월 중 일자 */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> dayOfMonth;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 월 중 일자를 반환한다.</span></span><br><span class="line"><span class="comment">    *</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span> 월 중 일자</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getDayOfMonth</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> dayOfMonth;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>개발자가 주석을 무시하는 습관에 빠지게 함 → 코드를 읽으며 주석을 건너뜀 → 코드 바뀌면서 주석은 거짓말이 됨</li><li>감정표현등 → 분풀이 주석으로 할 노력으로 코드 구조를 개선했었어야</li></ul><h3 id="무서운-잡음"><a href="#무서운-잡음" class="headerlink" title="무서운 잡음"></a>무서운 잡음</h3><ul><li>특정 오픈소스의 Javadocs에는 의미 없는 잡음이 존재</li></ul><h3 id="함수나-변수로-표현가능하면-주석을-달지-말라"><a href="#함수나-변수로-표현가능하면-주석을-달지-말라" class="headerlink" title="함수나 변수로 표현가능하면 주석을 달지 말라"></a>함수나 변수로 표현가능하면 주석을 달지 말라</h3><figure class="highlight java"><figcaption><span>주석 표현 버전</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 전역 목록 &lt;smodule&gt;에 속하는 모듈이 우리가 속한 하위 시스템에 의존하는가?</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">module</span>.getDependSubsystems().contains(subSysMod.getSubSystem()))</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>주석을 없애고 코드로 의미를 표현 버전</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ArrayList</span> <span class="variable">moduleDependees</span> <span class="operator">=</span> smodule.getDependSubSystems();</span><br><span class="line"><span class="type">String</span> <span class="variable">ourSubSystem</span> <span class="operator">=</span> subSysMod.getSubSystem();</span><br><span class="line"><span class="keyword">if</span> (moduleDependees.contains(ourSubSystem))</span><br></pre></td></tr></tbody></table></figure><h3 id="위치를-표시하는-주석"><a href="#위치를-표시하는-주석" class="headerlink" title="위치를 표시하는 주석"></a>위치를 표시하는 주석</h3><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Actions /////////////////////////////////////////////</span></span><br></pre></td></tr></tbody></table></figure><ul><li>가독성이 떨어짐, 뒷부분의 잡음 제거 필요</li><li>배너 : 눈에 띄며 주의 환기 → 반드시 필요할 때만 아주 드물게 사용할 것</li></ul><h3 id="닫는-괄호에-다는-주석"><a href="#닫는-괄호에-다는-주석" class="headerlink" title="닫는 괄호에 다는 주석"></a>닫는 괄호에 다는 주석</h3><ul><li>중괄호가 많아서 주석이 필요할 정도? 중첩이 심하고 장환한 함수라는 뜻</li><li>클린코드가 지향하는 작고 캡슐화된 함수에서 해당 주서은 잡음일 뿐</li><li>닫는 괄호에 주석을 달야야겠다는 생각이 든다면 함수를 작게 줄이려고 노력해야함</li></ul><h3 id="공로를-돌리거나-저자를-표시하는-주석"><a href="#공로를-돌리거나-저자를-표시하는-주석" class="headerlink" title="공로를 돌리거나 저자를 표시하는 주석"></a>공로를 돌리거나 저자를 표시하는 주석</h3><ul><li>코드 오염시키지 말아라</li><li>코드에 오랫동안 방치되어 점점 쓸모 없어지고 부정확한 정보로 변함</li><li>이런 정보는 소스 코드 관리 시스템에 저장할 것</li></ul><h3 id="주석으로-처리한-코드"><a href="#주석으로-처리한-코드" class="headerlink" title="주석으로 처리한 코드"></a>주석으로 처리한 코드</h3><figure class="highlight java"><figcaption><span>주석으로 처리한 코드는 절대 작성하지 말 것</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">InputStreamResponse</span> <span class="variable">response</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">InputStreamResponse</span>();</span><br><span class="line">response.setBody(formatter.getResultStream(), formatter.getByteCount());</span><br><span class="line"><span class="comment">// InputStream resultsStream = formatter.getResultStream();</span></span><br><span class="line"><span class="comment">// StreamReader reader = new StreamRead(resultsStream);</span></span><br><span class="line"><span class="comment">// response.setContent(reader.read(formatter.getByteCount()));</span></span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><figcaption><span>Apache commons의 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">this</span>.bytePos = writeBytes(pngIdBytes, <span class="number">0</span>);</span><br><span class="line"><span class="comment">//hdrPos = bytePos;</span></span><br><span class="line">writeHeader();</span><br><span class="line">writeResolution();</span><br><span class="line"><span class="comment">//dataPos = bytePos;</span></span><br><span class="line"><span class="keyword">if</span> (writeImageData()) {</span><br><span class="line">    wirteEnd();</span><br><span class="line">    <span class="built_in">this</span>.pngBytes = resizeByteArray(<span class="built_in">this</span>.pngBytes, <span class="built_in">this</span>.maxPos);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">    <span class="built_in">this</span>.pngBytes = <span class="literal">null</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="built_in">this</span>.pngBytes;</span><br></pre></td></tr></tbody></table></figure><ul><li>주석 처리한 코드 : 밉살스러운 관행<ul><li>이유가 있다고 남겼다고 사람들이 생각함 → 지우기 주저</li><li>점점 쓸모없는 코드 주석이 쌓여감</li><li>1960년대 에는 주석으로 처리한 코드가 유용</li><li>소스 관리 시스템이 알아서 코드를 기억해줌 → 코드 주석처리가 필요가 없음</li><li>그냥 코드를 삭제하라</li></ul></li></ul><h3 id="HTML-주석"><a href="#HTML-주석" class="headerlink" title="HTML 주석"></a>HTML 주석</h3><p>혐오 그 자체</p><ul><li>Javadocs에 html 태그 <pre>나 <p>태그 난리</p></pre></li><li>IDE에서 조차 읽기 어려움</li><li>Javadocs등의 도구로 주석을 뽑아 웹에 올리려면 해당 태그 삽입은 프로그래머가 아닌 도구가 책임</li></ul><h3 id="전역-정보"><a href="#전역-정보" class="headerlink" title="전역 정보"></a>전역 정보</h3><ul><li>(주석을 달아야 한다면) 근처 있는 코드만 기술</li><li>코드 일부에 주석을 달면서 시스템 정반적 정보 기술 X</li><li>아래 예시)<ul><li>이미 정보가 중복됨</li><li>주석은 기본 포트 정보를 기술</li><li>함수는 포트 기본값 통제 불가 → 이 주석은 밑의 함수가 아닌 다름 함수의 내용 설명</li><li>즉 포트 기본값 설정 코드가 변해도 이 주석이 변하리라는 보장이 없음<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 적합성 테스트가 동작하는 포트: 기본값은 &lt;b&gt;8082&lt;/b&gt;.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> fitnessePort</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setFitnessePort</span><span class="params">(<span class="type">int</span> fitnessePort)</span> {</span><br><span class="line">    <span class="built_in">this</span>.fitnewssePort = fitnessePort;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul></li></ul><h3 id="너무-많은-정보"><a href="#너무-많은-정보" class="headerlink" title="너무 많은 정보"></a>너무 많은 정보</h3><ul><li>주석에 스펙 역사나 관련없는 정보 장황하게 늘어놓지 마라</li></ul><h3 id="모호한-관계"><a href="#모호한-관계" class="headerlink" title="모호한 관계"></a>모호한 관계</h3><figure class="highlight java"><figcaption><span>Apache commons의 주석</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 모든 픽셀을 담을 만큼 충분한 배열로 시작한다(여기에 필터 바이트를 더한다).</span></span><br><span class="line"><span class="comment"> * 그리고 헤더 정보를 위해서 200 바이트를 더한다.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="built_in">this</span>.pngBytes = <span class="keyword">new</span> <span class="title class_">byte</span>[((<span class="built_in">this</span>. width + <span class="number">1</span>) * <span class="built_in">this</span>.height * <span class="number">3</span>) + <span class="number">200</span>];</span><br></pre></td></tr></tbody></table></figure><ul><li>위의 주석을 보고 드는 생각<ul><li>필터 바이트는 무엇?</li><li>+1과 관련이 있나? 아니면 *3과 관련이 있나? 아니면 둘다?</li><li>한 픽셀이 한 바이트인가?</li><li>200을 추가하는 이유는?</li></ul></li><li>주석 다는 목적 : 코드 만으로 설명이 부족해서</li><li>주석 자체가 다시 설명을 요구하는 안 좋은 주석</li></ul><h3 id="함수-헤더"><a href="#함수-헤더" class="headerlink" title="함수 헤더"></a>함수 헤더</h3><ul><li>짧은 함수 : 긴 설명이 필요 없음</li><li>짧고 한 가지만 수행하며 이름을 잘 붙인 함수 &gt;&gt;&gt;&gt;&gt; 주석으로 헤더를 추가한 함수</li></ul><h3 id="비공개-코드의-Javadocs"><a href="#비공개-코드의-Javadocs" class="headerlink" title="비공개 코드의 Javadocs"></a>비공개 코드의 Javadocs</h3><ul><li>공개 API에선 Javadocs가 유용</li><li>공개하지 않을 코드라면 Javadocs가 쓸모가 없음</li><li>시스템 내부에 속한 클래스와 함수에 Javadocs를 생성할 필요가 없음</li></ul><h3 id="예제"><a href="#예제" class="headerlink" title="예제"></a>예제</h3><p>예제)</p><ul><li>주석을 잘 달았다고 착각하게 만들지만 바람직하지 않은 코드<figure class="highlight java"><figcaption><span>주석을 잘 달았다고 착각하게 만들지만 바람직하지 않은 코드</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 이 클래스는 사용자가 지정한 최대 값까지 소수를 생성한다. 사용한 알고리즘은 에라스토테네스의 체다.</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 에라스토테네스: 기원전 276년 ...(후략)</span></span><br><span class="line"><span class="comment"> * &lt;/p&gt;</span></span><br><span class="line"><span class="comment"> * 알고리즘은 상당히 단순하다. 2에서 시작하는 정수 배열을 대상으로 2의 배수를 모두 제거한다.</span></span><br><span class="line"><span class="comment"> * 다음으로 남은 정수를 찾아 이 정수의 배수를 모두 지운다. 최대 값의 제곱근이 될 때까지 이를 반복한다.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> Alphonse</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 13 Feb 2002 atp</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GeneratePrimes</span> {</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * @param maxValue는 소수르 찾아낼 최대 값</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">     <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span>[] generatePrimes(<span class="type">int</span> maxValue) {</span><br><span class="line">         <span class="keyword">if</span> (maxValue &gt;= <span class="number">2</span>) {  <span class="comment">// 유일하게 유요한 경우</span></span><br><span class="line">             <span class="comment">// 선언</span></span><br><span class="line">             <span class="type">int</span> <span class="variable">s</span> <span class="operator">=</span> maxValue + <span class="number">1</span>;  <span class="comment">// 배열 크기</span></span><br><span class="line">             <span class="type">boolean</span>[] f = <span class="keyword">new</span> <span class="title class_">booleans</span>[s];</span><br><span class="line">             <span class="type">int</span> i;</span><br><span class="line">             </span><br><span class="line">             <span class="comment">// 배열을 참으로 초기화</span></span><br><span class="line">             <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; s; i++)</span><br><span class="line">                 f[i] = <span class="literal">true</span>;</span><br><span class="line">             </span><br><span class="line">             <span class="comment">// 소수가 아닌 알려진 숫자를 제거</span></span><br><span class="line">             f[<span class="number">0</span>] = f[<span class="number">1</span>] = <span class="literal">false</span>;</span><br><span class="line">             </span><br><span class="line">             <span class="comment">// 체</span></span><br><span class="line">             <span class="type">int</span> j;</span><br><span class="line">             <span class="keyword">for</span> (i = <span class="number">2</span>; i &lt; Math.sqrt(s) + <span class="number">1</span>; i++) {</span><br><span class="line">                 <span class="keyword">if</span> (f[i]) {  <span class="comment">// i가 남아 있는 숫자라면 이 숫자의 배수를 구한다.</span></span><br><span class="line">                     <span class="keyword">for</span> (j = <span class="number">2</span> * i; j &lt; s; j += i)</span><br><span class="line">                         f[j] = <span class="literal">false</span>;  <span class="comment">// 배수는 소수가 아니다.</span></span><br><span class="line">                 }</span><br><span class="line">             }</span><br><span class="line">             </span><br><span class="line">             <span class="comment">// 소수 개수는?</span></span><br><span class="line">             <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">             <span class="keyword">for</span> (i = <span class="number">0</span>; i &lt; s; i++) {</span><br><span class="line">                 <span class="keyword">if</span> (f[i])</span><br><span class="line">                     count++;  <span class="comment">// 카운트 증가</span></span><br><span class="line">             }</span><br><span class="line">             </span><br><span class="line">             <span class="type">int</span>[] primes = <span class="keyword">new</span> <span class="title class_">int</span>[count];</span><br><span class="line">             </span><br><span class="line">             <span class="comment">// 소수를 결과 배열로 이동한다.</span></span><br><span class="line">             <span class="keyword">for</span> (i = <span class="number">0</span>, j = <span class="number">0</span>; i &lt; s; i++) {</span><br><span class="line">                 <span class="keyword">if</span> (f[i])  <span class="comment">// 소수일 경우에</span></span><br><span class="line">                     primes[j++] = i;</span><br><span class="line">             }</span><br><span class="line">             </span><br><span class="line">             <span class="keyword">return</span> primes;  <span class="comment">// 소수를 반환한다.</span></span><br><span class="line">         }</span><br><span class="line">         <span class="keyword">else</span> <span class="comment">// maxValue &lt; 2</span></span><br><span class="line">                 <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];  <span class="comment">// 입력이 잘못되면 비어 있는 배열을 반환한다.</span></span><br><span class="line">     }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><figure class="highlight java"><figcaption><span>위의 코드의 리팩터링 결과</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 이 클래스는 사용자가 지정한 최대 값까지 소수를 구한다.</span></span><br><span class="line"><span class="comment"> * 알고리즘은 에라스토테네스의 체다.</span></span><br><span class="line"><span class="comment"> * 2에서 시작하는 정수 배열을 대상으로 작업한다.</span></span><br><span class="line"><span class="comment"> * 처음으로 남아 있는 정수를 찾아 배수를 모두 제거한다.</span></span><br><span class="line"><span class="comment"> * 배열에 더 이상 배수가 없을 때까지 반복한다.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PrimeGenerator</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">boolean</span>[] crossedOut;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span>[] result;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span>[] generatePrimes(<span class="type">int</span> maxValue) {</span><br><span class="line">        <span class="keyword">if</span> (maaxValue &lt; <span class="number">2</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line">        <span class="keyword">else</span> {</span><br><span class="line">            uncrossIntegersUpTo(maxValue);</span><br><span class="line">            crossOutMultiples();</span><br><span class="line">            putUncrossedIntegerIntoResult();</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">uncrossIntegersUpTo</span><span class="params">(<span class="type">int</span> maxValue)</span> {</span><br><span class="line">        crossedOut = <span class="keyword">new</span> <span class="title class_">boolean</span>[maxValue + <span class="number">1</span>];</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">2</span>; i &lt; crossedOut.length; i++)</span><br><span class="line">            crossedOut[i] = <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">crossOutMultiples</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">limit</span> <span class="operator">=</span> determineIterationLimit();</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">2</span>; i &lt;= limit; i++)</span><br><span class="line">            <span class="keyword">if</span> (notCrossed(i))</span><br><span class="line">                crossOutMultiplesOf(i);</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">determineIterationLimit</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 배열에 있는 모든 배수는 배열 크기의 제곱근보다 작은 소수의 인수다.</span></span><br><span class="line">        <span class="comment">// 따라서 이 제곱근보다 더 큰 숫자의 배수는 제거할 필요가 없다</span></span><br><span class="line">        <span class="type">dobule</span> <span class="variable">iterationLimit</span> <span class="operator">=</span> Math.sqrt(crossedOut.length);</span><br><span class="line">        <span class="keyword">return</span> (<span class="type">int</span>) iterationLimit;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">crossOutMultiplesOf</span><span class="params">(<span class="type">int</span> i)</span> {</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">multiple</span> <span class="operator">=</span> <span class="number">2</span> * i; multiple &lt; crossedOut.length; multiple += i)</span><br><span class="line">            crossedOut[multiple] = <span class="literal">true</span>;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">notCrossed</span><span class="params">(<span class="type">int</span> i)</span> {</span><br><span class="line">        <span class="keyword">return</span> crossedOut[i] == <span class="literal">false</span>;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">putUncrossedIntegersIntoResult</span><span class="params">()</span> {</span><br><span class="line">        result = <span class="keyword">new</span> <span class="title class_">int</span>[numberOfUncrossedIntegers()];</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>, i = <span class="number">2</span>; i &lt; crossedOut.length; i++)</span><br><span class="line">            <span class="keyword">if</span> (notCrossed(i))</span><br><span class="line">                result[j++] = i;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">nubmerOfUncrossedIntegers</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">2</span>; i &lt; crossedOut.length; i++)</span><br><span class="line">            <span class="keyword">if</span> (notCrossed(i))</span><br><span class="line">                count++;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> count;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="요약"><a href="#요약" class="headerlink" title="요약"></a>요약</h3><ul><li>주석을 최대한 쓰지 말자</li><li>쓰려면 좋은 주석</li><li>주석보다는 어노테이션</li><li>JavaDoc</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch4/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅲ. 함수</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch3/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch3/</guid>
      <pubDate>Mon, 03 Jan 2022 00:51:51 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅲ-함수&quot;&gt;&lt;a href=&quot;#Ⅲ-함수&quot; class=&quot;headerlink&quot; title=&quot;Ⅲ. 함수&quot;&gt;&lt;/a&gt;Ⅲ. 함수&lt;/h1&gt;&lt;p</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅲ-함수"><a href="#Ⅲ-함수" class="headerlink" title="Ⅲ. 함수"></a>Ⅲ. 함수</h1><p>함수</p><ul><li>프로그래밍 초창기 : 시스템을 루틴과 하위 루틴으로 나눔</li><li>포트란, PL/1 시절 : 프로그램, 하위 프로그램, 함수로 나눔</li><li>현재는 함수만 살아남음 : 가장 기본적 단위</li></ul><h2 id="함수-잘만드는-규칙"><a href="#함수-잘만드는-규칙" class="headerlink" title="함수 잘만드는 규칙"></a>함수 잘만드는 규칙</h2><h3 id="작게-만들어라"><a href="#작게-만들어라" class="headerlink" title="작게 만들어라"></a>작게 만들어라</h3><ul><li>첫째도 작게. 둘째도 더 작게!</li><li>블록과 들여쓰기<ul><li>if/while등에 들어가는 블록은 단 한줄이어야 한다</li><li>대게 여기서 함수 호출</li><li>wraping하는 enclosing func가 작아짐</li><li>블록안의 함수명이 적절하면 이해도 상승</li><li>함수의 들여쓰기는 1단이나 2단을 넘어서면 안된다</li></ul></li></ul><h3 id="한가지만-해라"><a href="#한가지만-해라" class="headerlink" title="한가지만 해라!"></a>한가지만 해라!</h3><p>함수에 대한 선배들의 충고</p><blockquote><ul><li>함수는 한 가지를 해야 한다</li><li>그 한가지를 잘해야 한다</li><li>그 한가지만을 해야한다</li></ul></blockquote><p>한가지의 범위 : TO</p><ul><li>ex) To RenderPageWithSetupsAndTeardowns<ul><li>페이지가 테스트 페이지인지 확인</li><li>테스트 페이지면 설정,해제 페이지를 넣는다</li><li>테스트 페이지임에 상관없이 HTML 렌더링</li></ul></li><li>지정된 함수 이름 아래 추상화 수준이 한단계 : 한 가지 작업만 하는 함수</li><li>섹션이 나누어지는 함수(선언,초기화,로직등)은 여러 작업을 한다는 증거</li></ul><h4 id="함수당-추상화-수준은-하나"><a href="#함수당-추상화-수준은-하나" class="headerlink" title="함수당 추상화 수준은 하나"></a>함수당 추상화 수준은 하나</h4><ul><li>함수 내 모든 문장의 추상화 수준이 동일해야 한다</li><li>다른 추상화 수준이 섞여 있으며 읽기에 헷갈림</li><li>내려가기 규칙<ul><li>위에서 아래로 이야기처럼 읽혀야 좋다</li><li>위에서 아래로 읽으면 함수 추상화가 한번에 한 단계씩 낮아짐</li></ul></li></ul><h3 id="Switch문-처리"><a href="#Switch문-처리" class="headerlink" title="Switch문 처리"></a>Switch문 처리</h3><p>siwtch문은 작게 만들기 어려움</p><ul><li>본질적으로 N가지 처리</li><li>블행히도 switch문을 완전히 피할 방법도 없음</li><li>예제 : 직원 유형에 따라 다른 값을 계산해 반환하는 함수<figure class="highlight java"><figcaption><span>Payroll.java</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Money <span class="title function_">calculatePay</span><span class="params">(Employee e)</span></span><br><span class="line"> <span class="keyword">throws</span> InvalidEmployeeType {</span><br><span class="line">     <span class="keyword">switch</span> (e.type) {</span><br><span class="line">         <span class="keyword">case</span> COMMISSIONED:</span><br><span class="line">            <span class="keyword">return</span> calculateCommissionedPay(e);</span><br><span class="line">         <span class="keyword">case</span> HOURLY:</span><br><span class="line">            <span class="keyword">return</span> calculateHourlyPay(e);</span><br><span class="line">         <span class="keyword">case</span> SALARIED:</span><br><span class="line">            <span class="keyword">return</span> calculateSalariedPay(e);</span><br><span class="line">         <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">InvalidEmployeeType</span>(e.type);</span><br><span class="line">     }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><ul><li>문제점<ol><li>함수가 길다. 새 회원 유형이 추가되면 더 길어짐</li><li>‘한 가지’작업만 수행하지 않음</li><li>SRP(Single Responsibility Principal) 위반 : 코드 변경 이유가 여럿</li><li>OCP(Open Closed Principal) 위반 : 새회원 유형 추가할 떄마다 코드 변경</li><li>위함수와 구조가 동일한 함수가 무한정 존재</li></ol><ul><li><code>isPayday(Employee e, Date date);</code></li><li><code>deliverPay(Employee e, Money pay);</code></li></ul></li><li>해결 방법 : 다형성 객체를 생성하는 코드로 상속관계로 숨긴다<ol><li>switch문을 추상 팩토리에 숨기고 아무에게 보여주지 않음</li><li>팩토리는 switch문을 이용해 적절한 Employee 파생 클래스의 인스턴스 생성</li><li>함수들(calculatePay, isPayday, deliverPay)는 Employee 인터페이스를 거쳐 호출 → 다형성(polymorphism)으로 실제 파생 클래스의 함수가 실행됨</li></ol></li></ul></li></ul><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Employee</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="type">boolean</span> <span class="title function_">isPayday</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> Money <span class="title function_">calculatePay</span><span class="params">()</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">deliverPay</span><span class="params">(Money pay)</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//-------------------------</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">EmployeeFactory</span> {</span><br><span class="line">    <span class="keyword">public</span> Employee <span class="title function_">makeEmployee</span><span class="params">(EmployeeRecord r)</span> <span class="keyword">throws</span> InvalidEmployeeType {</span><br><span class="line">        <span class="keyword">switch</span> (r.type) {</span><br><span class="line">         <span class="keyword">case</span> COMMISSIONED:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">CommissionedEmployee</span>(r);</span><br><span class="line">         <span class="keyword">case</span> HOURLY:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">HourlyEmployee</span>(r);</span><br><span class="line">         <span class="keyword">case</span> SALARIED:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SalariedEmployee</span>(r);</span><br><span class="line">         <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">InvalidEmployeeType</span>(r.type);</span><br><span class="line">       }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="서술적인-이름을-사용하라"><a href="#서술적인-이름을-사용하라" class="headerlink" title="서술적인 이름을 사용하라"></a>서술적인 이름을 사용하라</h3><ul><li>좋은 이름이 주는 가치는 매우 중요</li><li>길고 서술적인 이름이 짧고 어려운 이름보다 좋음</li><li>길고 서술적인 이름 &gt; 길고 서술적인 주석</li><li>좋은 이름을 고르고 코드를 더 좋게 재구성한 사례도 존재함</li><li>일관성 있는 네이밍<ul><li>모듈내의 함수 이름은 같은 문구/명사/동사 사용</li></ul></li></ul><h3 id="함수-인수"><a href="#함수-인수" class="headerlink" title="함수 인수"></a>함수 인수</h3><p>이상적 인수 : 0개</p><ul><li>3개: 가능한 피하라, 4개: 특별한 이유가 필요, 5개 특별한 이유가 있어도 안됨</li></ul><p>인수는 어렵다</p><ul><li>개념을 이해하기 어렵게 만듬</li><li>함수 이름과 인수 사이에 추상화 수준이 다름</li><li>코드 읽는 사람이 인수까지 파악해야함</li></ul><p>테스트 관점 더 어려움</p><ul><li>인수가 3개 이상이면 유효한 값으로 모든 조합 구성해 테스트하기 부담스럽다</li></ul><p>출력인수는 더 어렵다</p><ul><li>개발자는 함수에 인수로 입력을 넘기고 반환으로 출력을 받는것에 익숙</li></ul><p>최선은 0개의 인수, 차선은 1개의 인수</p><p>많이 쓰는 단항 형식</p><ul><li>가장 흔한 경우<ol><li>인수에 질문을 던지는 경우</li><li>인수를 근거해서 먼가를 변환해 결과를 반환</li></ol></li><li>흔하지 않지만 유용한 경우 : <strong>이벤트</strong><ul><li>이벤트는 입력 인수만 있고 출력 인수가 없음</li><li>이벤트는 이름과 문맥 주의해서 선택 : 이벤트라는 사실이 코드에 명확하게</li></ul></li><li>이외에는 가급적 단항 함수는 피할 것<ul><li>입력 인수를 변환하는 함수라면 변환 결과는 반환값으로 돌려줄 것</li></ul></li></ul><h4 id="플래그-인수"><a href="#플래그-인수" class="headerlink" title="플래그 인수"></a>플래그 인수</h4><ul><li>추하다</li><li>함수로 부울값을 넘기는 관례는 끔찍<ul><li>부울 값에 따라 하는 일이 다름 → 함수가 여러가지를 처리한다고 공표</li></ul></li></ul><h4 id="2항-함수"><a href="#2항-함수" class="headerlink" title="2항 함수"></a>2항 함수</h4><ul><li>2항 함수는 1항 함수보다 이해가 어려움</li><li>2항 함수가 적절한 경우 : 직교 좌표계 점 <code>Point p = new Point(0,0)</code></li><li><code>assertEquals(expected, actual)</code><ul><li>문제가 있음</li><li>바꿔쓰는 경우가 허다</li><li>자연적 순서가 없어서 인위적으로 기억해야 함</li></ul></li><li>2항이 무조건 나쁘다는 소리가 아님<ul><li>불가피하면 어쩔 수 없음</li><li>하지만 그만큼 위험이 따르는 부분이라는 것을 인지 필요</li><li>가능한 단항으로 바꾸도록 애써야 한다</li></ul></li></ul><h4 id="3항-함수"><a href="#3항-함수" class="headerlink" title="3항 함수"></a>3항 함수</h4><ul><li>2항보다 더 이해 어려움 - 신중히 고려 필요</li><li><code>assertEquals(expected, actual, message)</code><ul><li>메시지 무시 하는 오버로딩도 있음</li><li>expected 위치 자꾸 헷갈림</li></ul></li></ul><h4 id="인수-객체"><a href="#인수-객체" class="headerlink" title="인수 객체"></a>인수 객체</h4><ul><li>인수가 많다면 독자적인 클래스 변수로 선언?</li><li>VO나 DTO → 결국 개념을 표현</li></ul><h4 id="인수-목록"><a href="#인수-목록" class="headerlink" title="인수 목록"></a>인수 목록</h4><ul><li>3항이상의 가변 인수 의 경우 문제가 있음</li><li>동사와 키워드<ul><li>함수의 의도, 인수의 순서, 의도 표현 - 좋은 함수 이름 필 수</li><li>단항 함수 : 동사/명사 쌍</li><li>함수 이름에 키워드 추가 하는 형식도 있음</li><li><code>assertExpectedEqualsActual(expected,actual)</code> : 인수 순서 기억 필요 없음</li></ul></li></ul><h2 id="부수-효과를-일으키지마라"><a href="#부수-효과를-일으키지마라" class="headerlink" title="부수 효과를 일으키지마라"></a>부수 효과를 일으키지마라</h2><ul><li>Side effect는 거짓말 : 함수에서 1가지만 하겠다고 해놓고 다른 것도 함</li><li>많은 경우 시간적 결합(temporal coupling), 순서 종속성(order dependency) 초래</li></ul><h3 id="출력-인수"><a href="#출력-인수" class="headerlink" title="출력 인수"></a>출력 인수</h3><ul><li>인수를 출력으로 쓰지말아라<ul><li>기본적으로 우리는 인수를 함수 입력으로 해석</li><li>선언부를 보고 확인? 벌써 주춤 및 생산성 저하,</li></ul></li><li>객체 지향 언어에서는 출력 인수를 사용할 필요가 거의 없다<ul><li>이전 언어에는 출력인수가 불가피한 경우가 존재</li><li>객체 지향에서는 this가 존재</li><li>ex) <code>public void appendFooter(StringBuffer report)</code><ul><li><code>appendFooter(s);</code> : 출력을 인수에 넣었음</li><li>다음과 같이 쓸 수 있도록 고치자 : <code>report.appendFooter()</code></li></ul></li></ul></li><li>함수에서(굳이) 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하자.</li></ul><h2 id="명령과-조회-분리-x3D-CQS"><a href="#명령과-조회-분리-x3D-CQS" class="headerlink" title="명령과 조회 분리 = CQS"></a>명령과 조회 분리 = CQS</h2><ul><li>함수는 커맨드(무언가를 수행)하거나 쿼리(무언가에 답)하거나 둘중 하나만 할 것 <ol><li>코드 자체가 일단 괴상해짐(책의 내용처럼)</li><li>함수는 하나만 해야 하는 SRP를 지킬 수 있겠고</li><li>사이드 이펙트도 최소화할 수 있음</li></ol></li></ul><h2 id="오류-코드-보다는-예외-사용"><a href="#오류-코드-보다는-예외-사용" class="headerlink" title="오류 코드 보다는 예외 사용"></a>오류 코드 보다는 예외 사용</h2><ul><li><p>명령 함수에서 오류코드 반환 → CQS 약간 위반</p><ul><li>오류 코드 반환시 호출자는 오류 코드를 곧바로 처리해야 함 → 코드 복잡</li><li>예외를 사용하면 오류 처리가 원래 코드와 분리 → 코드가 깔끔해짐</li></ul></li><li><p>try-catch 뽑아내기</p><ul><li>추함 : 블록 자체가 코드 구조에 혼란을 주고 정상/오류 동작을 뒤섞는다</li><li>해당 블록을 별도 함수로 뽑아내자</li><li>catch 블록도 별도로 뽑아야 한다<ul><li>함수는 한가지 작업만 해야 한다</li><li>오류 처리도 ‘한 가지’ 작업에 속함</li><li>오류 처리 함수는 오류만 처리해야 함</li><li>함수에 try가 있다면 함수는 try로 시작해서 catch/finally로 끝나야 한다</li></ul></li></ul></li><li><p>Error.java 의존성 자석    </p><ul><li>오류코드를 반환한다 → (클래스,열거,변수 어떤 거든) 어디선가 오류 코드 정의한다</li><li>Error.java : 에러 코드를 모아놓은 enum class 라고 가정 → 의존성 자석<ul><li>다른 클래스에서 Error enum을 import</li><li>Error enum이 변하면 Error를 사용하는 모든 클래스 재컴파일 필요</li><li>재컴파일/재배치 번거로움 → 새오류 코드 정의 싫어함 → 기존 코드 재 사용;</li></ul></li><li>예외는 <code>Exception클래스</code>에서 파생 → 재컴파일/재배치 없이 새 예외 클래스 추가 가능</li></ul></li></ul><h2 id="반복하지-마라-DRY-Don’t-Repeat-Yourself-원칙"><a href="#반복하지-마라-DRY-Don’t-Repeat-Yourself-원칙" class="headerlink" title="반복하지 마라 : DRY(Don’t Repeat Yourself) 원칙"></a>반복하지 마라 : DRY(Don’t Repeat Yourself) 원칙</h2><p>중복 = 모든 악의 근원</p><ul><li>길이가 늘어남, 변경시 중복 된만큼 케어, 바쁘릴시 오류가 발생할 확율도 높음</li><li>많은 법칙/기법이 오직 중복을 없애거나 제어할 목적으로 존재<ul><li>RDMBS의 정규형 : 중복 제거</li><li>OOP : 코드를 부모 클래스로 몰아넣음 → 중복 제거</li><li>AOP, COP : 어떤 관점으로 보면 이들 모두 중복 제거 전략</li><li>하위 루틴 발명 이래로 모든 sw개발의 혁신은 중복을 제거하려는 지속적인 노력의 결과</li></ul></li></ul><h2 id="구조적-프로그래밍"><a href="#구조적-프로그래밍" class="headerlink" title="구조적 프로그래밍"></a>구조적 프로그래밍</h2><blockquote><p>모든 함수와 함수 내 모든 블록에 입구와 출구는 하나만 존재해야 한다</p><footer><strong>Edsger Dijkstra(에츠허르 데이크스트라)</strong><cite>Structured Programming(구조적 프로그래밍 원칙</cite></footer></blockquote><ul><li>함수는 return이 하나이어야 할 것</li><li>루프 안에서 break/continue 사용하면 안된다</li><li>goto는 절대로 사용하지 말 것</li></ul><p>위 규칙은 함수가 큰 경우에 한해서 상당한 이득</p><ul><li>함수를 아주 작게 만든다면 간혹 return/break/continue 여러번 사용 무방</li><li>때로는 오히려 단일 입/출구 규칙보다 의도 표현이 용이</li><li>goto는 큰함수에서만 의미 → 작은 함수에서 피해야 함</li></ul><h2 id="함수를-어떻게-짜는가"><a href="#함수를-어떻게-짜는가" class="headerlink" title="함수를 어떻게 짜는가?"></a>함수를 어떻게 짜는가?</h2><p>sw작성 = 글짓기</p><ul><li>글짓기<ul><li>먼저 기록을 기록후 읽기 좋게 다듬는다</li><li>초안은 대게 서투르고 어수선</li><li>말을 다듬고 문장을 고치고 문단을 정리</li><li>제대로 작성될 때까지</li></ul></li><li>함수 작성도 마찬가지<ul><li>처음엔 길고 복잡, 들여쓰기 단계도 많고 중복된 루프와 긴 인수 목록</li><li>네이밍도 즉흥적, 중복 코드도 많음</li><li>서투른 위의 코드를 빠짐없이 테스트 하는 단위 테스트 케이스 작성</li><li>이후 코드 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거</li><li>이 와중에 코드는 항상 단위 테스트를 통과</li><li>최종적으로 이 챕터의 규칙을 따르는 함수를 결과물로.</li><li>처음부터 짜내지 않는다. 그게 가능한 사람도 없다</li></ul></li></ul><h2 id="결론"><a href="#결론" class="headerlink" title="결론"></a>결론</h2><p>DSL</p><ul><li>특정 응용분야 시스템을 기술할 목적으로  설계된 도메인 특화언어</li></ul><p>프로그래머 대가</p><ul><li>시스템은 구현할 프로그램이 아니라 풀어갈 이야기로 여긴다</li><li>프로그램 언어를 수단으로 좀더 풍부하고 표현력이 강한 언어를 만들어 이야기를 풀어간다</li><li>재귀라는 기교로 시스템에 발생하는 동작은 그 도메인에 특화된 언어를 사용해 자신만의 이야기</li></ul><h2 id="3-7-규칙적용-코드-예시"><a href="#3-7-규칙적용-코드-예시" class="headerlink" title="3.7 규칙적용 코드 예시"></a>3.7 규칙적용 코드 예시</h2><figure class="highlight java"><figcaption><span>규칙적용 코드 예시</span></figcaption><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SetupTeardownIncluder</span> {</span><br><span class="line">  <span class="keyword">private</span> PageData pageData;</span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> isSuite;</span><br><span class="line">  <span class="keyword">private</span> WikiPage testPage;</span><br><span class="line">  <span class="keyword">private</span> StringBuffer newPageContent;</span><br><span class="line">  <span class="keyword">private</span> PageCrawler pageCrawler;</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">render</span><span class="params">(PageData pageData)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">return</span> render(pageData, <span class="literal">false</span>);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">render</span><span class="params">(PageData pageData, <span class="type">boolean</span> isSuite)</span></span><br><span class="line">    <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SetupTeardownIncluder</span>(pageData).render(isSuite);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="title function_">SetupTeardownIncluder</span><span class="params">(PageData pageData)</span> {</span><br><span class="line">    <span class="built_in">this</span>.pageData = pageData;</span><br><span class="line">    testPage = pageData.getWikiPage();</span><br><span class="line">    pageCrawler = testPage.getPageCrawler();</span><br><span class="line">    newPageContent = <span class="keyword">new</span> <span class="title class_">StringBuffer</span>();</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">render</span><span class="params">(<span class="type">boolean</span> isSuite)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="built_in">this</span>.isSuite = isSuite;</span><br><span class="line">    <span class="keyword">if</span> (isTestPage())</span><br><span class="line">      includeSetupAndTeardownPages();</span><br><span class="line">    <span class="keyword">return</span> pageData.getHtml();</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">isTestPage</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">return</span> pageData.hasAttribute(<span class="string">"Test"</span>);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includeSetupAndTeardownPages</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    includeSetupPages();</span><br><span class="line">    includePageContent();</span><br><span class="line">    includeTeardownPages();</span><br><span class="line">    updatePageContent();</span><br><span class="line">  }</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includeSetupPages</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">if</span> (isSuite)</span><br><span class="line">      includeSuiteSetupPage();</span><br><span class="line">    includeSetupPage();</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includeSuiteSetupPage</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    include(SuiteResponder.SUITE_SETUP_NAME, <span class="string">"-setup"</span>);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includeSetupPage</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    include(<span class="string">"SetUp"</span>, <span class="string">"-setup"</span>);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includePageContent</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    newPageContent.append(pageData.getContent());</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includeTeardownPages</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    includeTeardownPage();</span><br><span class="line">    <span class="keyword">if</span> (isSuite)</span><br><span class="line">      includeSuiteTeardownPage();</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includeTeardownPage</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    include(<span class="string">"TearDown"</span>, <span class="string">"-teardown"</span>);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">includeSuiteTeardownPage</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    include(SuiteResponder.SUITE_TEARDOWN_NAME, <span class="string">"-teardown"</span>);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">updatePageContent</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    pageData.setContent(newPageContent.toString());</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">include</span><span class="params">(String pageName, String arg)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="type">WikiPage</span> <span class="variable">inheritedPage</span> <span class="operator">=</span> findInheritedPage(pageName);</span><br><span class="line">    <span class="keyword">if</span> (inheritedPage != <span class="literal">null</span>) {</span><br><span class="line">      <span class="type">String</span> <span class="variable">pagePathName</span> <span class="operator">=</span> getPathNameForPage(inheritedPage);</span><br><span class="line">      buildIncludeDirective(pagePathName, arg);</span><br><span class="line">    }</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> WikiPage <span class="title function_">findInheritedPage</span><span class="params">(String pageName)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="keyword">return</span> PageCrawlerImpl.getInheritedPage(pageName, testPage);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> String <span class="title function_">getPathNameForPage</span><span class="params">(WikiPage page)</span> <span class="keyword">throws</span> Exception {</span><br><span class="line">    <span class="type">WikiPagePath</span> <span class="variable">pagePath</span> <span class="operator">=</span> pageCrawler.getFullPath(page);</span><br><span class="line">    <span class="keyword">return</span> PathParser.render(pagePath);</span><br><span class="line">  }</span><br><span class="line"> </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">buildIncludeDirective</span><span class="params">(String pagePathName, String arg)</span> {</span><br><span class="line">    newPageContent</span><br><span class="line">      .append(<span class="string">"\n!include "</span>)</span><br><span class="line">      .append(arg)</span><br><span class="line">      .append(<span class="string">" ."</span>)</span><br><span class="line">      .append(pagePathName)</span><br><span class="line">      .append(<span class="string">"\n"</span>);</span><br><span class="line">  }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="추가"><a href="#추가" class="headerlink" title="추가"></a>추가</h3><p>SOLID - 너무 유명한 법칙인데 계속 드문드문한 법칙</p><ul><li><p>SRP(Single Responsibility Principle)</p><ul><li>단일 책임의 원칙</li><li>THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE.</li><li><strong>한 클래스는 하나의 기능을 가진다</strong></li><li>클래스가 제공하는 모든 서비스는 그 하나의 책임을 수행하는데 집중되어야 한다</li><li><strong>어떤 변화에 의해 클래스를 변경해야 하는 이유도 오직 하나뿐이어야 한다</strong></li><li>적용시 책임영역이 확실 → 책임 변경에서 다른 책임의 변경의 연쇄를 막음</li><li>책임의 적절한 분배 → 코드 가독성 향상, 유지보수 용ㅇ이</li><li>다른 원리들을 적용하는 기초<ul><li>리팩토링이 필요한 위험사항 해법은 직/간접적으로 SRP원리와 연관</li><li>리팩토리 근본정신(항상 코드를 최상으로 유지)도 항상 객체 책임을 최상의 상태로 분배한다는 것에서 비롯되므로</li></ul></li><li>다른 것에 비해 단순하지만 실무에서 직접 적용해서 설계가 그리 쉽지는 않음</li><li>의도적인 많은 연습과 경험이 필요</li><li>무조건적인 책임 분리가 SRP가 적용되는 것이 아니다<ul><li>객체간의 응집력(cohesion)이 있다면 병합이 순작용의 수단 - 강 응집력 지향</li><li>결합력(coupling)이 있다면 분리가 순 작용의 수단 - 약 결합력 지향</li></ul></li></ul></li><li><p>OCP(Open Close Principal)</p><ul><li>개방폐쇄의 원칙</li><li>YOU SHOULD BE ABLE TO EXTEND A CLASSES BEHAVIOR, WITHOUT MODIFYING IT.</li><li><strong>sw 요소는 확장에 열려있고 변경에 닫혀있어야 한다</strong></li><li>객체지향 소프트웨어 설계라는 책에서 정의됨 - 1998, 버틀란트 메이어(Bertrand Meyer)</li><li>sw요소 : 컴포넌트, 클래스, 모듈, 함수</li><li>변경을 위한 비용은 최소화, 확장을 위한 비용은 극대화</li><li>객체지향의 장점을 극대화하는 아주 중요한 원리</li><li>요구사항 변경이나 추가 사항시<ul><li>기존 요소는 수정이 일어나지 말아야한다</li><li>기존 구성요소를 쉽게 확장해서 재사용할 수 있어야 한다</li><li>밥아저씨<ul><li>OCP는 관리 가능하고 재사용 가능한 코드를 만드는 기반</li><li>OCP를 가능하게 하는 중요 매커니믐 : 추상화, 다형성</li></ul></li></ul></li></ul></li><li><p>LSP (The Liskov Substitution principle)</p><ul><li>리스코프 치환원칙</li><li>FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT.</li><li><strong>서브 타입은 언제나 기반 타입으로 교체 가능해야 함</strong></li><li>서브 타입은 기반 타입이 약속한 규약을 포함하고 지켜야함</li><li>클래스의 상속, 인터페이스 상속 이용해 확장성 획득</li><li>다형성/확장성을 극대화하기 위해 인터페이스를 사용하는 것이 좋다<ul><li>상속을 통한 재사용은 기반/서브 사이에 IS-A 관계일 경우로만 제한 권장</li><li>부모클래스를 작성한 개발자가 세웠던 가정, 추론 과정, 원리를 정확히 이해 필요<br>이는 자식 클래스가 부모클래스에게 강하게 결합된 의미</li><li>상속은 부모와 자식 클래스의 결합도가 매우 높음</li><li>상속은 추상화를 이용을 해야 위의 문제가 해결됨<ul><li>자식의 클래스가 부모 클래스의 구현이 아닌 추상화에 의존하도록</li></ul></li></ul></li><li>합성(컴포지션) 사용 가능 : 이펙티브 자바 18번 아이템<ul><li>구글에서 “이펙티브 자바 18” 검색</li><li>데코레이터 패턴 처럼</li></ul></li></ul></li><li><p>ISP (Interface Segregation Principle)</p><ul><li>인터페이스 분리 원칙</li><li>CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE.</li><li><strong>자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다</strong></li><li>가능한 최소한의 인터페이스만 구현</li><li>특정 클래스 이용하는 클라이언트가 여러 개이며 각각 클래스의 특정 부분만 이용?<br>→ 여러 인터페이스로 분류해서 클라이언트가 필요한 기능만 전달한다</li><li>SRP가 클래스의 단일 책임이라면? ISP는 인터페이스의 단일 책임</li></ul></li><li><p>DIP (Dependency Inversion Principle)</p><ul><li>의존성 역전의 법칙<ul><li>A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.</li><li>B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS.</li></ul></li><li><strong>상위 모델은 하위 모델의 의존하면 안되며 둘다 추상화에 의존해야 한다</strong></li><li><strong>추상화는 세부사항에 의존해선 안되며 세부사항은 추상화에 따라 달라진다</strong></li><li>하위 모델의 변경이 상위 모듈의 변경을 요구하는 위계 관계를 끊는다</li><li>실제 사용 관계는 그대로이나 추상화를 매개로 메시지를 주고 받으며 관계를 느슨하게</li></ul></li><li><p>추가 참조</p><ul><li>그림으로 보는 : <a href="https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898">https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898</a></li><li><a href="https://www.nextree.co.kr/p6960">https://www.nextree.co.kr/p6960</a></li></ul></li></ul><p>간결한 함수</p><ul><li>분기는 가능한 추상 클래스 or Factory<br>안전한 함수 - Side Effect</li></ul><p>함수 리팩터링</p><ol><li>기능을 구현하는 서투른 함수를 작성</li><li>테스트 코드 작성</li><li>리팩터링</li></ol><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch3/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅱ.의미 있는 이름</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch2/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch2/</guid>
      <pubDate>Sun, 02 Jan 2022 00:00:52 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅱ-의미-있는-이름&quot;&gt;&lt;a href=&quot;#Ⅱ-의미-있는-이름&quot; class=&quot;headerlink&quot; title=&quot;Ⅱ. 의미 있는 이름</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅱ-의미-있는-이름"><a href="#Ⅱ-의미-있는-이름" class="headerlink" title="Ⅱ. 의미 있는 이름"></a>Ⅱ. 의미 있는 이름</h1><p>이름을 잘 짖는 간단한 몇가지 규칙</p><ol><li>의도를 분명히 밝혀라</li></ol><ul><li>변수명이나 메서드 명에서 정확하게 의도를 알 수 있는 이름</li></ul><ol start="2"><li>그릇된 정보를 피하라</li></ol><ul><li>그릇된 단서 : 널리 쓰이는 단어를 다른 의미로 사용, 자료구조 이름을 다른 뜻으롷 사용하는 등</li><li>역시 이름만 바꾸면 깨끗이 풀리는 문제들</li></ul><ol start="3"><li>의미 있게 구분하라</li></ol><ul><li>컴파일러/인터프리터만 통과하려는 식의 구현 : 의미 없는 이름 혹은 고민하지 않은 이름들</li><li>이름이 달라야 한다면 의미도 달라져야한다</li><li>읽는 사람이 차이를 알도록 이름을 지어야 한다</li></ul><ol start="4"><li>발음하기 쉬운 이름을 사용</li></ol><ul><li>발음하기 어려운 이름은 커뮤니케이션도 어려움</li></ul><ol start="5"><li>검색하기 쉬운 이름을 사용</li></ol><ul><li>예제 책등에서 보이는 문자 하나를 사용하는 이름/상수는 쉽게 눈에 띄지 않는다</li><li>ex) e는 영어에서 가장 많이 쓰이는 문자 → 검색을 해서 follow하기가 불가능</li><li>검색하기 쉬운 이름이 상수보다 좋다</li><li><strong>이름 길이는 범위 크기에 비례해야한다</strong><ul><li>아주 간단한 메서드의 로컬 변수는 한 문자 사용 가능</li><li>변수나 상수를 코드 여러 곳에서 사용한다면 그만큼 검색하기 쉬운 이름이 바람직</li></ul></li></ul><ol start="6"><li>인코딩을 피하라</li></ol><ul><li>이름에 인코딩(!)할 정보는 어차피 아주 많음</li><li>인코딩한 이름은 거의 발음하기 어려우며 오타가 생긴다</li><li>대표적인 예시 : 이제 필요없는 기법들<ul><li>헝가리식 표기법(인코딩 대표방법)</li><li>멤버 변수 접두어</li></ul></li><li>필요한 경우 : 인터페이스와 구현 클래스<ul><li>굳이 하나를 인코딩해야 한다면 구현 클래스의 이름 ex)~~~FactoryImp</li></ul></li></ul><ol start="7"><li>자신의 기억력을 자랑하지 마라</li></ol><ul><li>문자 하나만 사용 피하라<ul><li>루프의 반복횟수를 세는 변수 i,j,k는 ok(l은 절대안된다!)    </li><li>최악 이미 a와 b를 사용하므로 c를 사용</li></ul></li><li>프로그래머들으 대체로 똑똑 : 자신의 능력을 과시하고 싶음</li><li>똑똑한 vs 전문가 : 전문가는 명료함이 최고라는 사실을 이해한다</li></ul><ol start="8"><li>클래스 이름</li></ol><ul><li>클래스 이름, 객체 이름은 명사,명사구가 적합</li><li>Manager, Processor, Data, Info등의 단어는 피하고 동사 사용 X</li></ul><ol start="9"><li>메서드 이름</li></ol><ul><li>메서드 이름은 동사,동사구가 적합</li><li>접근자,변경자,조건자는 javabean표준에 따라 get,set,is를 붙인다</li><li>Construct를 overload할때는 정적 팩토리 메서드를 사용한다(생성자 제한으로 private해버리기)</li></ul><ol start="10"><li>기발한 이름은 피하라</li></ol><ul><li>유머코드, 농담 코드를 넣지 말자</li><li>특정 문화에서만 사용되는 단어/농담은 피하라</li><li>재미난 이름보다는 명료한 이름을 선택 : 의도분명, 솔직하게 표현</li></ul><ol start="11"><li>한 개념에 한 단어 사용</li></ol><ul><li>추상적 개념 하나에 한 단어 고수 : 아니면 어느 클래스에서 어떤 단어를 썼는지 알기 어려움</li><li>동일 코드에 다른 이름? 읽는 사람은 클래스도 다르고 타입도 다르리가고 생각함</li></ul><ol start="12"><li>말장난 금지</li></ol><ul><li>한 단어를 2가지 목적으로 사용하지 마라</li><li>한 개념에 한 단어를 사용하라 하더라도 기존과 맥락이 다르다면 이름을 다르게 해야한다</li><li>집중적 탐구가 아니라 대충 훑어봐도 이해할 코드 작성이 목표</li></ul><ol start="13"><li>문제 영역에서 가져온 이름 사용하라</li></ol><ul><li>문제영역과 해법영역을 구분할 것</li><li>적절한 프로그래머 용어가 없는 문제 영역 개념과 관련이 깊은 코드라면 문제영역에서 이름을 가져온다</li><li>유지보수 프로그매러가 도메인 전문가에게 의미를 물어 파악 가능</li></ul><ol start="14"><li>의미 있는 맥락을 추가</li></ol><ul><li>스스로 의미 있는 이름 먼저 → 클래스, 함수, 이름공간에 넣어 맥락 부여 → 전부 실패하면 최후의 방법으로 접두어</li></ul><ol start="15"><li>불필요한 맥락을 없애라</li></ol><ul><li>의미가 분명한 경우라면 짧은 이름이 긴 이름보다 좋다 : 불필요한 맥락 추가X</li></ul><h3 id="구글-자바-스타일-가이드"><a href="#구글-자바-스타일-가이드" class="headerlink" title="구글 자바 스타일 가이드"></a>구글 자바 스타일 가이드</h3><ul><li>p31 인터페이스 클래스와 구현 클래스 부분<ul><li>밥 아저씨는 <code>ShapeFactoryImp</code>가 좋다고 하지만 <code>CircleFactory</code>가 훨씬 좋음</li></ul></li><li>구글 자바 스타일 가이드에서 5번째 : <a href="https://google.github.io/styleguide/javaguide.html">https://google.github.io/styleguide/javaguide.html</a><ol><li>패키지 이름 : 모두 소문자, 언더스코어 없이</li><li>클래스 이름</li></ol><ul><li>UpperCamelCase(대문자 시작)</li><li>명사/명사구</li><li>인터페이스도 명사/명사구이지만 때때로 형용사/형용사구 ex)<code>Readable</code><br>(그러면 클래스는 <code>ClassImplReadable</code> 이런식으로) </li><li>테스트 클래스는 <code>Test</code>로 끝나야 한다</li></ul><ol start="3"><li>메서드 이름</li></ol><ul><li>lowerCamelCase(소문자 시작)</li><li>동사/동사구</li><li>JUnit test 메서드에서 한해서 언더스코어 등장<ul><li>패턴 : <code>&lt;methodUnderTest&gt;_&lt;state&gt;</code>, for example <code>pop_emptyStack</code></li></ul></li></ul><ol start="4"><li>상수 이름</li></ol><ul><li>상수 : 완전 불변,ㅡ 메서드 감지 가능한 부작용 없는 static final</li><li>인스턴스의 상태가 변경될 수 있는 경우 상수가 아님 : 단순히 객체변형을 막는것이 목적이 아니다</li><li>CONSTANT_CASE : 모두 대문자, 언더스코어(_)로 각 단어 구분</li></ul><ol start="5"><li>상수가 아닌 필드 이름</li></ol><ul><li>lowerCamelCase</li><li>정적이나 다른것들 ㅂ포함</li></ul><ol start="6"><li>파라미터</li></ol><ul><li>lowerCamelCase</li><li>public 메서드일때 한 글자 파라미터 이름은 피해야 한다</li></ul><ol start="7"><li>지역 변수</li></ol><ul><li>lowerCamelCase</li><li>final, 불변이어도 지역변수는 상수로 간주 되지 않으며, 상수 스타일 지정하지 말아야</li></ul><ol start="8"><li>Type 변수 이름</li></ol><ul><li>단일 대문자, 추가적으로 한자리 숫자가 따라올수 있음(<code>E</code>,<code>T</code>,<code>X</code>,<code>T2</code>)</li><li>클래스 명명 규칙+ T : (<code>RequestT</code>, <code>FooBarT</code>)</li></ul></li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch2/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>[Clean Code] Ⅰ. 깨끗한 코드</title>
      <link>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch1/</link>
      <guid>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch1/</guid>
      <pubDate>Sat, 01 Jan 2022 00:41:38 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;Clean Code 3판을 읽고 정리한 글입니다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;Ⅰ-깨끗한-코드&quot;&gt;&lt;a href=&quot;#Ⅰ-깨끗한-코드&quot; class=&quot;headerlink&quot; title=&quot;Ⅰ. 깨끗한 코드&quot;&gt;&lt;/a&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>Clean Code 3판을 읽고 정리한 글입니다</p></blockquote><h1 id="Ⅰ-깨끗한-코드"><a href="#Ⅰ-깨끗한-코드" class="headerlink" title="Ⅰ. 깨끗한 코드"></a>Ⅰ. 깨끗한 코드</h1><p>오프닝</p><ul><li>사소한 곳에서 발휘하는 정직은 사소하지 않다.</li><li>신은 세세함에 깃들어 있다</li><li>SW는 80%가 유지보수</li></ul><h2 id="코드는-언제나-존재"><a href="#코드는-언제나-존재" class="headerlink" title="코드는 언제나 존재"></a>코드는 언제나 존재</h2><p>코드는 사라지지 않음</p><ul><li>코드 : 요구사항 상세히 표현하는 수단</li><li>하위 어느 수준에 다르면 코드 없이 요구사항 표현이 불가능</li><li>기계가 실행할 정도의 상세하게 요구사항을 명시하는 작업 : coding</li><li>추상화가 아무리 높아져도 코드가 사라지진 않음</li><li>요구사항을 모호하게 주어도 정확한 프로그램 생성? : 절대 불가능</li><li>인간도 고객의 감정으로는 성공적인 시스템 구현 불가</li><li>제대로 명시한 요구사항 : 코드만큼 정형적 → 테스트 케이스로 사용가능</li></ul><h3 id="bad-code"><a href="#bad-code" class="headerlink" title="bad code"></a>bad code</h3><blockquote><p>이 책은 좋은 코드가 중요하다는 다소 미약한 전제에 기반한다</p><footer><strong>Kent Beck</strong><cite><a href="http://www.acornpub.co.kr/book/kentpattern">Implementation Patterns</a></cite></footer></blockquote><ul><li>밥아저씨 : <strong>미약하지 않으며 아주 아주 중요하다</strong></li><li>wading(고행) : 나쁜 코드를 헤쳐나가는 작업 → 나쁜 코드는 회사가 문을 닫을 수 있을 정도의 치명적</li><li>르블랑의 법칙 : 나중은 결코 오지 않는다<ul><li>일단 급하게 짜고 나중에 손본다고 생각하지 말고 처음 짤 때부터 잘 해야한다</li></ul></li></ul><h3 id="나쁜-코드로-치르는-대가"><a href="#나쁜-코드로-치르는-대가" class="headerlink" title="나쁜 코드로 치르는 대가"></a>나쁜 코드로 치르는 대가</h3><ul><li>나쁜 코드는 개발 속도 저하 유발</li><li>나쁜 코드가 쌓일 수록 팀 생산성 저하되며 나중에 0에 수렴</li><li>인력을 추가 투입?<ul><li>추가 인력은 기존 설계와 히스토리를 잘 모름</li><li>설계를 잘 모름 → 의도에 맞는 변경과 반하는 변경 구분 못함</li><li>추가 인력은 생산성 향상 푸쉬 압박 시달림</li><li>결론 : 나쁜 코드를 더 많이 양산 → 다시 생산성 0</li></ul></li></ul><h3 id="원대한-재설계의-꿈"><a href="#원대한-재설계의-꿈" class="headerlink" title="원대한 재설계의 꿈"></a>원대한 재설계의 꿈</h3><ul><li>팀이 재설계 요청, 관리층은 싫지만 어쩔수 없이 허락</li><li>두팀의 경주 시작  : 타이거팀-유능하고 똑똑한 TF팀, 기존팀-현재 시스템 유지보수팀</li><li>경주는 10년넘게 걸리는 경우도 있음</li><li>어마어마한 비용</li></ul><h3 id="태도"><a href="#태도" class="headerlink" title="태도"></a>태도</h3><ul><li>관리자의 일정과 요구사항을 강력히 푸쉬 → 그들의 책임</li><li>프로그래머들의 책임 : 좋은 코드를 사수하는 것</li><li>나쁜 코드가 초래하는 프로젝트 실패는 프로그래머들의 큰 책임이 있음</li><li>의사 비유: 환자(상사)가 요구해도 의사는 위험한 부분 단호히 거부</li><li>프로그래머도 마찬가지 : 나쁜 코드의 위험을 이해 못하는 관리자 말을 그대로 따르는 것은 전문가가 아님</li></ul><h3 id="원초적-난제"><a href="#원초적-난제" class="headerlink" title="원초적 난제"></a>원초적 난제</h3><ul><li>빨리 가려고 클린 코드 유지 X → 나쁜 코드 양산 → 생산성 저하, 기간 늘어남 → 결국 늦게 감</li></ul><h3 id="클린코드라는-예술"><a href="#클린코드라는-예술" class="headerlink" title="클린코드라는 예술?"></a>클린코드라는 예술?</h3><ul><li>클린 코드 구현 행위 = 그림을 그리는 행위와 비슷<ul><li>그림을 보면 대부분의 사람은 잘 그려졌는지 엉망으로 그려졌는지 안다</li><li>그림을 잘 구분한다고 그림을 잘 그리진 않는다</li><li>clean code 와 bad code를 구분하더라도 clean code를 작성할 수 있다는 것은 아님</li></ul></li><li>clean code는 ‘코드 감각’을 필요로 함<ul><li>‘청결’이라는 습득한 감각을 이용해 자잘한 기법들을 적용하는 절제와 규율 필요</li><li>절제와 규율을 적용해 클린 코드로 바꾸는 전략도 파악</li></ul></li><li>코드 감각이 있는 프로그래머는 나쁜 모듈을 보면<ul><li>좋은 모듈로 개선할 방안을 떠올린다</li><li>‘코드 감각’으로 최고 방안을 선택</li><li>여정을 계획</li></ul></li></ul><h3 id="클린-코드의-정의"><a href="#클린-코드의-정의" class="headerlink" title="클린 코드의 정의"></a>클린 코드의 정의</h3><h2 id="밥-아저씨들이-인터뷰한-수많은-guru들의-클린코드에-대한-주옥같은-정의와-주석들"><a href="#밥-아저씨들이-인터뷰한-수많은-guru들의-클린코드에-대한-주옥같은-정의와-주석들" class="headerlink" title="밥 아저씨들이 인터뷰한 수많은 guru들의 클린코드에 대한 주옥같은 정의와 주석들"></a>밥 아저씨들이 인터뷰한 수많은 guru들의 클린코드에 대한 주옥같은 정의와 주석들</h2><h2 id="보이스카우트-규칙"><a href="#보이스카우트-규칙" class="headerlink" title="보이스카우트 규칙"></a>보이스카우트 규칙</h2><ul><li>미국 보이스카우트 규칙 : 캠프장은 처음 왔을 떄보다 더 깨끗하게 해놓고 떠나라</li><li>이를 반영 : 체크아웃을 할 때 보다 (아주 작게라도) 더 깨끗한 코드를 체크인 해라</li></ul><h2 id="정리"><a href="#정리" class="headerlink" title="정리"></a>정리</h2><p>bad code</p><ul><li>성능이 나쁜 code : 불필요한 연산이 들어가서 개선의 여지가 있는 코드</li><li>의미가 모호한 코드 : 이해하기 어려운 코드, 네이밍과 내용이 다른 코드</li><li>중복된 코드 : 비슷한 내용인데도 중복된 코드 → 버그 양산</li></ul><p>clean code</p><ul><li>성능이 <strong>좋은</strong> 코드</li><li>의미가 <strong>명확한</strong> 코드 : 가독성 좋은 코드</li><li><strong>중복이 제거된</strong> 코드</li></ul><h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><ul><li><a href="/dev-book/cleancode/CleanCode-ch1/" title="[Clean Code] Ⅰ. 깨끗한 코드">[Clean Code] Ⅰ. 깨끗한 코드</a></li><li><a href="/dev-book/cleancode/CleanCode-ch2/" title="[Clean Code] Ⅱ.의미 있는 이름">[Clean Code] Ⅱ.의미 있는 이름</a></li><li><a href="/dev-book/cleancode/CleanCode-ch3/" title="[Clean Code] Ⅲ. 함수">[Clean Code] Ⅲ. 함수</a></li><li><a href="/dev-book/cleancode/CleanCode-ch4/" title="[Clean Code] Ⅳ. 주석">[Clean Code] Ⅳ. 주석</a></li><li><a href="/dev-book/cleancode/CleanCode-ch5/" title="[Clean Code] Ⅴ. 형식 맞추기">[Clean Code] Ⅴ. 형식 맞추기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch6/" title="[Clean Code] Ⅵ. 객체와 자료구조">[Clean Code] Ⅵ. 객체와 자료구조</a></li><li><a href="/dev-book/cleancode/CleanCode-ch7/" title="[Clean Code] Ⅶ. 오류 처리">[Clean Code] Ⅶ. 오류 처리</a></li><li><a href="/dev-book/cleancode/CleanCode-ch8/" title="[Clean Code] Ⅷ. 경계">[Clean Code] Ⅷ. 경계</a></li><li><a href="/dev-book/cleancode/CleanCode-ch9/" title="[Clean Code] Ⅸ. 단위 테스트">[Clean Code] Ⅸ. 단위 테스트</a></li><li><a href="/dev-book/cleancode/CleanCode-ch10/" title="[Clean Code] Ⅹ. 클래스">[Clean Code] Ⅹ. 클래스</a></li><li><a href="/dev-book/cleancode/CleanCode-ch11/" title="[Clean Code] Ⅺ. 시스템">[Clean Code] Ⅺ. 시스템</a></li><li><a href="/dev-book/cleancode/CleanCode-ch12/" title="[Clean Code] Ⅻ. 창발성(創發性)">[Clean Code] Ⅻ. 창발성(創發性)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch13/" title="[Clean Code] XIII. 동시성">[Clean Code] XIII. 동시성</a></li><li><a href="/dev-book/cleancode/CleanCode-ch14/" title="[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)">[Clean Code] XIV. 점진적 개선(SUCCESSIVE REFINEMENT)</a></li><li><a href="/dev-book/cleancode/CleanCode-ch15/" title="[Clean Code] XV. JUnit 들여다보기">[Clean Code] XV. JUnit 들여다보기</a></li><li><a href="/dev-book/cleancode/CleanCode-ch16/" title="[Clean Code] XVI. SerialDate 리팩터링">[Clean Code] XVI. SerialDate 리팩터링</a></li><li><a href="/dev-book/cleancode/CleanCode-ch17/" title="[Clean Code] XVII. 냄새와 휴리스틱">[Clean Code] XVII. 냄새와 휴리스틱</a></li><li><a href="/dev-book/cleancode/CleanCode-review/" title="[Clean Code] 다 읽었다~">[Clean Code] 다 읽었다~</a></li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/">dev-book</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev-book/cleancode/">cleancode</category>
      
      
      <category domain="http://rkaehdaos.github.io/tags/cleancode/">cleancode</category>
      
      
      <comments>http://rkaehdaos.github.io/dev-book/cleancode/CleanCode-ch1/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>spring-mvc-RedirectAttributes</title>
      <link>http://rkaehdaos.github.io/dev/Spring/Spring-MVC/spring-mvc-RedirectAttributes/</link>
      <guid>http://rkaehdaos.github.io/dev/Spring/Spring-MVC/spring-mvc-RedirectAttributes/</guid>
      <pubDate>Mon, 22 Nov 2021 08:57:10 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Related-POST&quot;&gt;&lt;a href=&quot;#Related-POST&quot; class=&quot;headerlink&quot; title=&quot;Related POST&quot;&gt;&lt;/a&gt;Related POST&lt;/h2&gt;&lt;h3 id=&quot;intro&quot;&gt;&lt;a href=&quot;#intro&quot; c</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><h3 id="intro"><a href="#intro" class="headerlink" title="intro"></a>intro</h3><ul><li>원래 잘 사용하고 있는 어플리케이션에 변경 요청 사항이 생김</li><li><code>addAttribute</code>로 끝내는 건줄 알고 까봤더니 마지막 view가 “redirect”접두어로 리다이렉트</li><li>Redirect는 이전 요청이 끊어지고 새로운 GET 요청 시작</li><li>따라서 Redirect 실행 이전의 모델 데이터는 소멸하므로 모델전달은 의미가 없음</li></ul><h3 id="RedirectAttributes"><a href="#RedirectAttributes" class="headerlink" title="RedirectAttributes"></a>RedirectAttributes</h3><ul><li>스프링이 제공</li><li>모든 플래시 속성을 세션에 복사</li><li>리다이렉션 이후 저장된 플래시 속성을 세션에서 모델로 이동</li></ul><h3 id="RedirectAttributes의-2-메서드"><a href="#RedirectAttributes의-2-메서드" class="headerlink" title="RedirectAttributes의 2 메서드"></a>RedirectAttributes의 2 메서드</h3><ul><li><code>addAttribute</code></li><li><code>addFlashAttribute</code></li><li>차이점<ul><li><code>addAttribute</code>로 전달한 값은 GET의 URL의 쿼리스트링으로 붙으며 리프레시해도 유지됨</li><li><code>addFlashAttribute</code> 전달값은 URL에 노출되지 않으며 리프레시 할 경우 데이터가 소멸(1회성)</li></ul></li></ul><h3 id="마무리"><a href="#마무리" class="headerlink" title="마무리"></a>마무리</h3><ul><li>MockMvc에서<code>MockMvcResultMatchers.flash()</code>로 플래시를 받아와서 플래시 속성에 접근 가능</li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev/">dev</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev/Spring/">Spring</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev/Spring/Spring-MVC/">Spring_MVC</category>
      
      
      
      <comments>http://rkaehdaos.github.io/dev/Spring/Spring-MVC/spring-mvc-RedirectAttributes/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>lombok</title>
      <link>http://rkaehdaos.github.io/dev/tools/lombok/</link>
      <guid>http://rkaehdaos.github.io/dev/tools/lombok/</guid>
      <pubDate>Mon, 08 Nov 2021 01:56:49 GMT</pubDate>
      
        
        
      <description>&lt;h2 id=&quot;Related-POST&quot;&gt;&lt;a href=&quot;#Related-POST&quot; class=&quot;headerlink&quot; title=&quot;Related POST&quot;&gt;&lt;/a&gt;Related POST&lt;/h2&gt;&lt;h1 id=&quot;롬복-정리&quot;&gt;&lt;a href=&quot;#롬복-정리&quot; c</description>
        
      
      
      
      <content:encoded><![CDATA[<h2 id="Related-POST"><a href="#Related-POST" class="headerlink" title="Related POST"></a>Related POST</h2><h1 id="롬복-정리"><a href="#롬복-정리" class="headerlink" title="롬복 정리"></a>롬복 정리</h1><h2 id="1-Opening"><a href="#1-Opening" class="headerlink" title="1. Opening"></a>1. Opening</h2><p>이미 5년도 넘게 쓰고 있는 롬복을 세삼스레 정리하게 된 것은 이펙티브 자바와 JPA 공부덕이다<br>세삼스럽다.</p><h2 id="2-롬복이란"><a href="#2-롬복이란" class="headerlink" title="2. 롬복이란"></a>2. 롬복이란</h2><ul><li>롬복을 이용하면 특정 코드를 줄여서 가독성과 편의성을 올릴 수 있다</li><li>특히 Getter와 Setter를 엄청나게 줄일 수 있어서 애용했었는데..<br>오히려 그럼으로써 getter/setter를 남용하게 되는 원인이기도 하다</li></ul><h2 id="3-Getter-x2F-Setter"><a href="#3-Getter-x2F-Setter" class="headerlink" title="3. @Getter/@Setter"></a>3. @Getter/@Setter</h2><ul><li>게터메서드, 세터 메서드 생성</li><li>필드가 아닌 클래스에 선언하는 경우 해당 클래스의 모든 필드의 게터,세터 생성</li><li>VO 클래스를 만들게 되는 경우 노가다에서 해방</li></ul><h2 id="4-생성자"><a href="#4-생성자" class="headerlink" title="4. 생성자"></a>4. 생성자</h2><ul><li><code>@NoArgsConstructor</code> : 파라미터 없는 기본 생성자 생성</li><li><code>@AllArgsConstructor</code> : 모든 필드 값을 파라미터로 받는 생성자</li><li><code>@RequiredArgsConstructor</code><ul><li><code>final</code>, <code>@NonNull</code> 인 필드값만 파라미터로 받는 생성</li><li>사실 서비스나 컨트롤러도 이걸로 생성하는게 가장 깔끔하다<ul><li>어차피 컨트롤러는 서비스, 서비스는 JPA리포지토리등을 주입받음</li><li>중간에 빈을 바꿀 일은 없음 : 처음 빈이 로딩될때 전부 설정값 반영됨</li><li>그러면 해당 빈을 final로 하고 이 어노테이션을 사용하면 생성자 주입 방식 사용됨</li></ul></li></ul></li><li><code>@NonNull</code> : 메서드 파라미터에 사용, null이 파라미터로 전달되면 예외</li><li>access 필드로 접근 권한 수정 가능</li><li><code>@NoArgsConstructor(access = AccessLevel.PROTECTED)</code><ul><li>JPA 기본 스펙상 기본 생성자 필요</li><li>protected로 제어하는 것까지 허용되며 new로 객체생성함을 막을 수 있음</li></ul></li></ul><h2 id="5-ToString"><a href="#5-ToString" class="headerlink" title="5. @ToString"></a>5. @ToString</h2><ul><li>이클립스나 인텔리제이가 만들어주는 ToString을 자동으로 생성</li><li>롬복을 사용하기전에는  Apache Commons Lang 라이브러리의 ToStringBuilder를 사용</li><li>보통 문제가 되는게 JPA 양방향 관계에서 잘못 사용하면 무한 순환해서 스택이 터지는 오류</li><li>그래서 개인적으로 exclude로 특정 필드 제외하는 것을 애용 했음</li><li>공부를 해보니 <code>of</code>를 권장 : <code>exclude</code>보다 새로 객체를 추가해 줄 떄 마다 수정 비용이 적다고 한다</li></ul><h2 id="6-EqualsAndHashCode"><a href="#6-EqualsAndHashCode" class="headerlink" title="6. @EqualsAndHashCode"></a>6. @EqualsAndHashCode</h2><ul><li>equals(), hashcode()를 자동으로 생성해줌</li><li>ToString,처럼 exclude, callSuper 사용 가능</li><li>객체 직렬화에서 제외할때 사용하는  transient 키워드 사용시 exclude 효과</li><li>callSuper : 해당 속성을 통해 부모 클래스 필드까지 동일한지 체크 가능</li><li>성능 이슈가 발생할 수 있다고 한다(Set 자료 구조)</li></ul><h2 id="7-Data"><a href="#7-Data" class="headerlink" title="7. @Data"></a>7. @Data</h2><ul><li>@Getter, @Setter, @RequiredArgsConstructor, @ToString, @EqualsAndHashCode를 한번에</li><li>사실 조금만 공부하면 실무에서는 절대 사용하지 않아야 한다.</li></ul><h2 id="8-Builder"><a href="#8-Builder" class="headerlink" title="8. @Builder"></a>8. @Builder</h2><ul><li>GoF 빌더 패턴을 간단하게 만들 수 있다</li><li>클래스에 설정시 <code>@AllArgsConstructor</code>효과가 생기므로 기피</li><li>생성자 빌더 패턴 적용시 장점<ul><li>필요한 데이터만 설정 가능<ul><li>일반 생성자 사용시 필요없는 값에 더미값을 넣거나 해당 값이 없는 생성자를 새로 적용 필요</li><li>빌더 사용시 동적으로 처리 : 객체 생성 용이, 코드양 줄임</li></ul></li><li>유연성 확보<ul><li>파라미터 추가시 기존 코드 수정 필요. 생성 코드가 100개면 100개에 따른 수정이 필요</li><li>빌더 패턴 이용시 기존 코드에 영향을 주지 않음</li></ul></li><li>가독성 확보<ul><li>파라미터 증가시 리딩 효율 급격히 떨어짐</li><li>빌더 패턴 적용시 가독성 유지, 직관적으로 어떤 데이터 설정인지 파악 가능</li></ul></li><li>불변성 확보<ul><li>Setter 사용은 불필요한 확장 가능성<ul><li>Open-Closed 위배</li><li>불필요한 코드리딩 필요</li></ul></li><li>따라서 클래스 변수를 final로 선언하고 객체의 생성은 빌더에 맡겨야 한다</li></ul></li></ul></li><li>advance<ul><li><code>builderClassName</code> 파라미터로 nested 빌더 클래스의 이름을 (클래스명Builder가 기본)</li><li><code>builderMethodName</code>으로 빌더 클래스를 반환하는 static 메소드의 이름을 (builder()가 기본)</li><li><code>buildMethodName</code>으로 객체를 반환하는 빌드 메소드의 이름 (build()가 기본)을 설정할 수 있다</li><li><code>@Builder.Default</code> : 기본값 설정, 해당 값이 없으면 정해진 기본값을 토태로 빌더가 실행된다</li><li><code>@Singular</code> : List등의 컬렉션에 사용<ul><li>builder에서 값을 추가할 떄 사용되는 메서드 이름 입력</li><li>빈 컬렉션이 자동으로 생성되며, 이 컬렉션은 수정 불가</li></ul></li><li><code>@Builder(toBuilder = true)</code><ul><li>인스턴스에서도 builder 가능.</li><li>해당 인스턴스에서 일부 값만 변경한 새로운 객체 생성 가능</li></ul></li></ul></li></ul><h2 id="9-Cleanup"><a href="#9-Cleanup" class="headerlink" title="9. @Cleanup"></a>9. @Cleanup</h2><ul><li><code>try-with-resource</code>구문과 비슷한 효과</li><li>차이점<ul><li><code>try-with-resource</code> : 구문이 종료될 때 <code>AutoCloseable</code>인터페이스의 close()호출</li><li><code>@Cleanup</code> : scope 종료시 어노테이션이 설정된 변수의 close() 호출</li></ul></li></ul><h2 id="10-val"><a href="#10-val" class="headerlink" title="10. val"></a>10. val</h2><ul><li>스칼라의 val 키워드 기능</li><li>객체 타입을 추론한 불변 값 선언</li><li>스클라와 달리 지역변수, foreach 구문에만 사용 가능</li><li>메소드 파라미터, 클래스 필드에 사용 불가</li></ul><h2 id="11-Value"><a href="#11-Value" class="headerlink" title="11. @Value"></a>11. @Value</h2><ul><li>@Data어노테이션과 비슷하지만 필드를 변경할 수 없는 불변 객체를 만듬</li><li>예시<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Value</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestValve</span> {</span><br><span class="line">    String name;</span><br><span class="line">    <span class="type">int</span> val1;</span><br><span class="line">    <span class="type">int</span> val2;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li>빌드되면 다음과 같아진다<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">TestValve</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> val1;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> val2;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">TestValve</span><span class="params">(<span class="keyword">final</span> String name, <span class="keyword">final</span> <span class="type">int</span> val1, <span class="keyword">final</span> <span class="type">int</span> val2)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        <span class="built_in">this</span>.val1 = val1;</span><br><span class="line">        <span class="built_in">this</span>.val2 = val2;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getVal1</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.val1;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getVal2</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.val2;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(<span class="keyword">final</span> Object o)</span> {</span><br><span class="line">        <span class="keyword">if</span> (o == <span class="built_in">this</span>) {</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        } <span class="keyword">else</span> <span class="keyword">if</span> (!(o <span class="keyword">instanceof</span> TestValve)) {</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        } <span class="keyword">else</span> {</span><br><span class="line">            <span class="type">TestValve</span> <span class="variable">other</span> <span class="operator">=</span> (TestValve)o;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.getVal1() != other.getVal1()) {</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">this</span>.getVal2() != other.getVal2()) {</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            } <span class="keyword">else</span> {</span><br><span class="line">                <span class="type">Object</span> <span class="variable">this$name</span> <span class="operator">=</span> <span class="built_in">this</span>.getName();</span><br><span class="line">                <span class="type">Object</span> <span class="variable">other$name</span> <span class="operator">=</span> other.getName();</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>$name == <span class="literal">null</span>) {</span><br><span class="line">                    <span class="keyword">if</span> (other$name != <span class="literal">null</span>) {</span><br><span class="line">                        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                    }</span><br><span class="line">                } <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">this</span>$name.equals(other$name)) {</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                }</span><br><span class="line"></span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            }</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">int</span> <span class="variable">PRIME</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> result * <span class="number">59</span> + <span class="built_in">this</span>.getVal1();</span><br><span class="line">        result = result * <span class="number">59</span> + <span class="built_in">this</span>.getVal2();</span><br><span class="line">        <span class="type">Object</span> <span class="variable">$name</span> <span class="operator">=</span> <span class="built_in">this</span>.getName();</span><br><span class="line">        result = result * <span class="number">59</span> + ($name == <span class="literal">null</span> ? <span class="number">43</span> : $name.hashCode());</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> {</span><br><span class="line">        <span class="type">String</span> <span class="variable">var10000</span> <span class="operator">=</span> <span class="built_in">this</span>.getName();</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"TestValve(name="</span> + var10000 + <span class="string">", val1="</span> + <span class="built_in">this</span>.getVal1() + <span class="string">", val2="</span> + <span class="built_in">this</span>.getVal2() + <span class="string">")"</span>;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><code>@Value</code>는 val 언노테이션 사용 → val 어노테이션 사용 가능할 때만 <code>@Value</code> 사용 가능</li></ul><h2 id="12-Log"><a href="#12-Log" class="headerlink" title="12 @Log"></a>12 @Log</h2><ul><li>클래스 상단의 static final log 필드 자동 생성</li><li>SLF4J 사용하는 경우 @Slf4j를 사용하면 된다.</li></ul><h2 id="13-Synchronized"><a href="#13-Synchronized" class="headerlink" title="13. @Synchronized"></a>13. @Synchronized</h2><ul><li><code>Synchronized</code>보다 더 자세한 설정이 가능한 어노테이션</li><li><code>Synchronized</code> :  static, insttance 단위로 락</li><li><code>@Synchronized</code>: @파라미터로 입력받는 오브젝트 단위</li><li>파라미터가 없는 경우 어노테이션이 사용된 메소드 단위 락<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestValve</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">OBJECT_LOCK</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    String name;</span><br><span class="line">    String name2;</span><br><span class="line">    <span class="type">int</span> value;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//파라미터로 입력받는 Object 단위 Lock</span></span><br><span class="line">    <span class="meta">@Synchronized("OBJECT_LOCK")</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">valCalc</span><span class="params">(<span class="type">int</span> value2)</span>{</span><br><span class="line">        <span class="keyword">return</span> value + value2;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Synchronized("OBJECT_LOCK")</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getValue</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Synchronized</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Synchronized</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName2</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> name2;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure></li></ul><h1 id="롬복-사용시-부모-생성자-호출-불가"><a href="#롬복-사용시-부모-생성자-호출-불가" class="headerlink" title="롬복 사용시 부모 생성자 호출 불가"></a>롬복 사용시 부모 생성자 호출 불가</h1><ul><li>레퍼런스 : <a href="https://stackoverflow.com/questions/29740078/how-to-call-super-constructor-in-lombok">https://stackoverflow.com/questions/29740078/how-to-call-super-constructor-in-lombok</a></li><li>롬복에서 Parent에서 <code>@AllArgsConstructor</code></li><li>Parent를 extend한 Child에서 <code>super</code>로 부모 생성자 호출 불가</li><li>리플랙션 사용시 슈퍼 클래스의 생성자를 호출할 수 없다</li></ul><h2 id="일반적인-경우"><a href="#일반적인-경우" class="headerlink" title="일반적인 경우"></a>일반적인 경우</h2><ul><li>생성자에 builder 어노테이션을 추가하면 super 가능<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Parent</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String parentName;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> parentAge;</span><br><span class="line">}</span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Child</span> <span class="keyword">extends</span> <span class="title class_">Parent</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String childName;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> childAge;</span><br><span class="line">    <span class="meta">@Builder</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Child</span><span class="params">(String parentName, <span class="type">int</span> parentAge, String childName, <span class="type">int</span> childAge)</span> {</span><br><span class="line">        <span class="built_in">super</span>(parentName, parentAge);</span><br><span class="line">        <span class="built_in">this</span>.childName = childName;</span><br><span class="line">        <span class="built_in">this</span>.childAge = childAge;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ul><h2 id="답변-갱신"><a href="#답변-갱신" class="headerlink" title="답변 갱신"></a>답변 갱신</h2><ul><li><code>@SuperBuilder</code>을 사용해서 상속되는 클래스 빌더 생성 가능</li><li>Lombok 1.18.2 이후부터 사용 가능</li></ul>]]></content:encoded>
      
      
      <category domain="http://rkaehdaos.github.io/categories/dev/">dev</category>
      
      <category domain="http://rkaehdaos.github.io/categories/dev/tools/">tools</category>
      
      
      
      <comments>http://rkaehdaos.github.io/dev/tools/lombok/#disqus_thread</comments>
      
    </item>
    
  </channel>
</rss>
