LeeA0의 공부 일기

[Clean Code] 4장. 주석 본문

Java/Clean Code

[Clean Code] 4장. 주석

LeeA0 2021. 9. 16. 22:35

[사담]

더보기

다음주면 벌써 추석이네요~! ㅎㅎㅎ

명절동안 미뤄두었던 알고 정리랑 클린코드 정리를 하고 싶네요~

평소에 알고리즘 문제를 풀고, 주석을 하나하나 남겨 놓는게 좋은 건줄 알았는 데, 이번 장을 읽으면서 생각이 바뀌었습니다..ㅎㅎ 앞으로 필요한 정보만 주석으로 남기도록해야겠네요!

 

[정리]

나쁜 코드에 주석을 달지 마라. 새로 짜라.

 

잘 달린 주석 경솔하고 근거없고 오래된 주석
그 어떤 정보보다 유용 코드를 이해하기 어렵게 만듬
거짓과 잘못된 정보를 퍼트림

- 만약, 프로그래밍 언어 자체가 표현력이 풍부하다면? 주석은 필요하지 않다.

- 주석은 코드로 의도를 표현하는 것에 실패했을 때, 이를 만회하기 위해 사용하는 것이다.

- 부정확한 주석은 아예 없는 주석보다 훨씐 더 나쁘다.

=> 진실은 오직 코드에만 존재한다!

 

<안 좋은 예시>

MockRequest request;
private final String HTTP_DATE_REGEXP = 
    "[SMTWF][a-z]{2}\\,\\s[0-9]\\s[JFMASOND][a-z]{2}\\s[0-9]{4}\\s[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}\\sGMT";
private Response response;
private FitNesseContext context;
private FileResponder responder;
private Locale saveLocale;
// Example: "Tue, 02 Apr 2003 22:18:49 GMT"

-> 분명 HTTP_DATE_REGEXP에 달린 주석이었겠지만, 중간에 다른 변수가 추가되면서 낙동강 오리알이 되어버렸다..!

 

<주석은 나쁜 코드를 보완하지 못한다>

코드에 주석을 추가하는 이유 : 코드 품질이 나쁨 -> 이를 보완하기 위해 주석을 담

=> 아니다! 코드를 정리해야한다!

=> 코드로 의도를 표현해라!

 

<안 좋은 예시>

// 직원에게 복지 혜택을 받을 자격이 있는지 검사한다.
if((employee.flags & HOURLY_FLAG) && (employee.age > 65))

<좋은 예시>

if(employee.isEligibleForFullBenefits())

 

<좋은 주석>

1. 법적인 주석

- 각 소스 파일 첫머리에 주석으로 들어가는 저작권 정보와 소유권 정보

- 모든 조항과 조건을 열거하는 대신에 가능하다면 표준 라이선스나 외부 문서를 참조

 

<예시>

// Copyright (C) 2003,2004,2005 Object Mentor, Inc. All rights reserved.
// GNU General Public License 버전 2이상을 따르는 조건으로 배포한다.

 

2. 정보를 제공하는 주석

- 기본적인 정보를 제공

 

<예시>

// 테스트 중인 Responder 인스턴스를 반환한다.
protected abstract Responder responderInstance();

-> 함수 이름을 responderBeingTested로 바꾸면 주식이 필요없음

 

<더 나은 예시>

// kk:mm:ss EEE, MMM dd, yyy 형식이다.
Pattern timeMatcher = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w*, \\d*, \\d*");

-> 시간과 날짜를 변환하는 클래스를 만들어 코드를 옮기면 더 좋음

 

3. 의도를 설명하는 주석

- 구현을 이해하게 도와주는 선을 넘어 결정에 깔린 의도까지 설명

 

<안 좋은 예시>

public int compareTo(Object o){
    if(o instanceof WikiPagePath){
        WikiPagePath p = (WikiPagePath) o;
        String compressedName = StringUtil.join(names, "");
        String compressedArgumentName = StringUtil.join(p.names, "");
        return compressedName.compareTo(compressedArgumentName);
    }
    return 1; // 오른쪽 유형이므로 정렬 순위가 더 높다.
}

<좋은 예시>

public void testConcurrentAddWidgets() throws Exception {
    WidgetBuilder widgetBuilder = new WidgetBuilder(new Class[]{BoldWidget.class});
    String text = "'''bold text'''";
    ParentWidget parent = new BoldWidget(new MockWidgetRoot(), "'''bold text'''");
    AtomicBoolean failFlag = new AtomicBoolean();
    failFlag.set(false);
    
    // 스레드를 대량 생성하는 방법으로 어떻게든 경쟁 조건을 만들려 시도한다.
    for(int i = 0; i < 25000; i++){
        WidgetBuilderThread widgetBuilderThread = 
            new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
        Thread thread = new Thread(widgetBuilderThread);
        thread.start();
    }
    assertEquals(false, failFlag.get());
}

 

4. 의미를 명료하게 밝히는 주석

- 인수나 반환값이 표준 라이브러리나 변경하지 못하는 코드에 속할 때 사용

 

<예시>

public void testCompareTo() throws Exception{
    WikiPagePath a = PathParser.parse("PageA");
    WikiPagePath ab = PathParser.parse("PageA.PageB");
    WikiPagePath b = PathParser.parse("PageB");
    WikiPagePath aa = PathParser.parse("PageA.PageA");
    WikiPagePath bb = PathParser.parse("PageB.PageB");
    WikiPagePath ba = PathParser.parse("PageB.PageA");
    
    assertTrue(a.compareTo(a) == 0);    // a == a
    assertTrue(a.compareTo(b) != 0);    // a != b
    assertTrue(ab.compareTo(ab) == 0);    // ab == ab
    assertTrue(a.compareTo(b) == -1);    // a < b
    assertTrue(aa.compareTo(ab) == -1);    // aa < ab
    assertTrue(ba.compareTo(bb) == -1);    // ba < bb
    assertTrue(b.compareTo(a) == 1);    // b > a
    assertTrue(ab.compareTo(aa) == 1);    // ab > aa
    assertTrue(bb.compareTo(ba) == 1);    // bb > ba
}

-> 그릇된 주석을 달을 위험이 상당히 높음

 

5. 결과를 경고하는 주석

- 다른 프로그래머에게 결과를 경고할 목적으로 사용

 

<괜찮은 예시>

// 여유 시간이 충분하지 않다면 실행하지 마십시오.
public void _testWithReallyBigFile(){
    writeLinesToFile(10000000);
    
    response.setBody(testFile);
    response.readyToSend(this);
    String responseString = output.toString();
    assertSubString("Content-Length: 1000000000", responseString);
    assertTrue(byteSent > 1000000000);
}

-> 특정 테스트 케이스를 꺼야하는 이유를 설명

-> JUnit4가 나오기 전에는 메서드 이름 앞에 _를 쓰는 것이 일반적인 관례였음

-> 요즘에는 @Ignore 속성을 이용해 이유를 서술하고, 테스트 케이스를 끔

 

<좋은 예시>

public static SimpleDateFormat makeStandardHttpDateFormat(){
    // SimpleDateFormat은 스레드에 안전하지 못하다.
    // 따라서 각 인스턴스를 독립적으로 생성해야 한다.
    SimpleDateFormat df = new SimpleDateFormat("EEE, dd MMM yyy HH:mm:ss z");
    df.setTimeZone(TimeZone.getTimeZone("GMT"));
    return df;
}

 

6. TODO 주석

- 앞으로 할 일을 남겨둔 주석

- 프로그래머가 필요하다 여기지만 당장 구현하기 어려운 업무를 기술

But. 시스템에 나쁜 코드를 남겨 놓는 핑계가 되서는 안 됨

 

<예시>

// TODO-MdM 현재 필요하지 않다.
// 체크아웃 모델을 도입하면 함수가 필요 없다.
protected VersionInfo makeVersion() throws Exception{
    return null;
}

 

7. 중요성을 강조하는 주석

- 무언가의 중요성을 강조하기 위해 사용

 

<예시>

String listItemContent = match.group(3).trim();
// 여기서 trim은 정말 중요하다. trim 함수는 문자열에서 시작 공백을 제거한다.
// 문자열에 시작 공백이 있으면 다른 문자열로 인식되기 때문이다.
new ListItemWidget(this, listItemContent, this.level + 1);
return buildList(text.substring(match.end()));

 

8. 공개 API에서 Javadocs

 

<나쁜 주석>

1. 주절거리는 주석

- 특별한 이유없이 의무감으로 혹은 프로세스에서 하라고 하니까 마지못해 달은 주석

 

<예시>

public void loadProperties(){
    try{
        String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
        FileInputStream propertiesStream = new FileInputStream(propertiesPath);
        loadedProperties.load(propertiesStream);
    }
    catch(IOException e){
        // 속성 파일이 없다면 기본 값을 모두 메모리로 읽어 들었다는 의미이다.
    }
}

 

2. 같은 이야기를 중복하는 주석

- 헤더에 달린 주석이 같은 코드 내용을 그대로 중복하는 주석

 

<예시1>

// this.closed가 true일 때 반환되는 유틸리티 메서드다.
// 타임아웃에 도달하면 예외를 던진다.
public synchronized void waitForClose(final long timeoutMillis) throws Exception{
    if(!closed){
        wait(timeoutMillis);
        if(!closed){
            throw new Exception("MockResponseSender could not be closed");
        }
    }
}

-> 주석이 코드보다 더 많은 정보를 제공하지 못함

 

<예시2>

public abstract class ContainerBase 
    implements Container, Lifecycle, Pipeline, MBeanRegistration, Serializable{
    /**
     * 이 컴포넌트의 프로세서 지연값
     */
    protected int backgroundProcessorDelay = -1;
    
    /**
     * 이 컴포넌트를 지원하기 위한 생명주기 이벤트
     */
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
    
    /**
     * 이 컴포넌트를 위한 컨테이너 이벤트 Listener
     */
    protected ArrayList listeners = new ArrayList();
    
    /**
     * 컨테이너와 관련된 Loader 구현
     */
    protected Loader loader = null;
     
    /**
     * 컨테이너와 관련된 Logger 구현
     */
    protected Log logger = null;
     
     ...
}

-> 코드만 지저분하고 정신없게 만들 뿐, 기록이라는 목적에 전혀 기여하지 못함

 

3. 오해할 여지가 있는 주석

 

<예시>

// this.closed가 true일 때 반환되는 유틸리티 메서드다.
// 타임아웃에 도달하면 예외를 던진다.
public synchronized void waitForClose(final long timeoutMillis) throws Exception{
    if(!closed){
        wait(timeoutMillis);
        if(!closed){
            throw new Exception("MockResponseSender could not be closed");
        }
    }
}

-> this.closed가 true로 변하는 순간에 메서드가 반환되는 지, true일 때 반환되는지 애매하다.

-> true일 때 반환되거나 타임아웃이 되고 this.closed가 true가 아니면 예외를 던진다.

-> 경솔하게 이 함수를 호출하면 자기 코드가 왜 굼벵이 기어가듯 돌아가는지 모른다!!

 

4. 의무적으로 다는 주석

- 모든 함수에 Javadocs를 달거나 모든 변수에 주석을 달은 것

-> 코드를 헷갈리게 만들고, 거짓말할 가능성을 높이며, 잘못된 정보를 제공할 여지만 만듬

 

<예시>

/**
 *
 * @param title CD 제목
 * @param author CD 저자
 * @param tracks CD 트랙 숫자
 * @param durationInMinutes CD 길이(단위: 분)
 */
 public void addCD(String title, String author, int tracks, int durationInMinutes){
     CD cd = new CD();
     cd.title = title;
     cd.author = author;
     cd.tracks = tracks;
     cd.duration = durationInMinutes;
     cdList.add(cd);
 }

 

5. 이력을 기록하는 주석

- 모듈을 편집할 때마다 모듈 첫머리에 추가한 주석

-> 지금은 소스 코드 관리 시스템이 있어서 제거하는 편이 좋다.

 

<예시>

* 변경 이력 (11-Oct-2001부터)
* -----------------------------
* 11-Oct-2001 : 클래스를 다시 정리하고 새로운 패키지인 com.jrefinery.date로 옮겼다 (DG);
* 05-Nov-2001 : getDescription() 메서드를 추가했으며 NotableDate class를 제거했다 (DG);
* 12-Nov-2001 : IBD가 setDescription() 메서드를 요구한다. NotableDate 클래스를 없앴다 (DG);
*               getPreviousDayOfWeek(), getFollowingDayOfWeek(), getNearestDayOfWeek()를 변경해
*               버그를 수정했다(DG);
* 05-Dec-2001 : SpreadsheetDate 클래스에 존재하는 버그를 수정했다 (DG);
* 29-May-2002 : month 상수를 독자적인 인터페이스로 옮겼다 (MonthConstants) (DG);
* 27-Aug-2002 : addMonths() 메서드에 있는 버그를 수정했다. N???levka Petr 덕분이다 (DG);
 ...

 

6. 있으나 마나 한 주석

- 너무 당연한 사실을 언급하며 새로운 정보를 제공하지 못하는 주석

 

<예시1>

/**
 * 기본 생성자
 */
protected AnnualDateRule(){
}

/** 월 중 일자 */
private int dayOfMonth;

/**
 * 월 중 일자를 반환한다.
 *
 * @return 월 중 일자
 */
public int getDayOfMonth(){
    return dayOfMonth;
}

-> 개발자가 주석을 무시하는 습관에 빠짐

-> 코드를 읽으며 자동으로 주석을 건너뜀

-> 코드가 바뀌면서 거짓말 주석이 됨

 

<예시2>

private void startSending(){
    try{
        doSending();
    }
    catch(SocketException e){
        // 정상. 누군가 요청을 멈췄다.
    }
    catch(Exception e){
        try{
            response.add(ErrorResponder.makeExceptionString(e));
            response.closeAll();
        }
        catch(Exception e1){
            // 이게 뭐야!
        }
    }
}

-> 첫번째 주석은 catch블록을 무시해도 괜찮은 이유를 설명하는 주석

-> 두번째 주석은 블록을 짜다 짜증이 났는지 주석을 막 달았다.

 

<개선한 예시>

private void startSending(){
    try{
        doSending();
    }
    catch(SocketException e){
        // 정상. 누군가 요청을 멈췄다.
    }
    catch(Exception e){
        addExceptionAndCloseResponse(e);
    }
}
private void addExceptionAndCloseResponse(Exception e){
    try{
        response.add(ErrorResponder.makeExceptionString(e));
        response.closeAll();
    }
    catch(Exception e1){
    }
}

-> 있으나 마나한 주석을 없애고, try/catch 블록을 독자적인 함수로 만듬

 

7. 무서운 잡음

- 문서를 제공해야 한다는 잘못된 욕심으로 탄생하는 잡음

- 때로는 Javadocs도 잡음

=> 함수나 변수로 표현할 수있다면 주석을 달지마라

 

<예시1>

/** The name. */
private String name;

/** The version. */
private String version;

/** The licenceName. */
private String licenceName;

/** The version. */
private String info;

 

<예시2>

// 전역 목록 <smodule>에 속하는 모듈이 우리가 속한 하위 시스템에 의존하는가?
if(smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))

 

<개선한 예시>

ArrayList moduleDependees = smodule.getDependSubsystems();
String ourSubSystem = subSysMod.getSubSystem();
if(moduleDependees.contains(ourSubSystem))

-> 주석을 변수로 변경

 

8. 위치를 표시하는 주석

- 소스 파일에서 특정 위치를 표시하려 사용한 주석

- 가독성만 낮추는 주석

 

<예시>

// Actions ////////////////////////////////

 

9. 닫는 괄호에 다는 주석

- 닫는 괄호에 달은 특수한 주석

- 중첩이 심하고 장황하다면 의미가 있지만, 캡슐화된 함수에는 잡음임

=> 닫는 괄호에 주석을 달지말고, 함수를 줄이려고 노력하자!

 

<예시>

public class wc{
    public static void main(String[] args){
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        String line;
        int lineCount = 0;
        int charCount = 0;
        int wordCount = 0;
        try{
            while((line = in.readLine()) != null){
                lineCount++;
                charCount += line.length();
                String words[] = line.split("\\W");
                wordCount += words.length;
            } //while
            System.out.println("wordCount = " + wordCount);
            System.out.println("lineCount = " + lineCount);
            System.out.println("charCount = " + charCount);
        } //try
        catch (IOException e) {
            System.err.println("error:" + e.getMessage());
        } //catch
    } //main
}

 

10. 공로를 돌리거나 저자를 표시하는 주석

=> 소스코드 관리 시스템에 저장해라!

 

<예시>

/* 릭이 추가함 */

 

11. 주석으로 처리한 코드

 

<예시>

InputStreamResponse response = new InputStreamResponse();
response.setBody(formatter.getResultStream(), formatter.getByteCount());
// InputStream resultsStream = formatter.getResultStream();
// StreamReader reader = new StreamReader(resultsStream);
// response.setContent(reader.read(formatter.getByteCount()));

-> 주석으로 처리된 코드는 '이유가 있어 남겨놓은 것', '중요한 것'이라고 생각해 지우기를 주저함

-> 쓸모 없는 코드가 쌓임

 

12. HTML 주석

- HTML주석은 편집기/IDE에서조차 읽기 어렵다.

 

<예시>

/**
 * 적합성 테스트를 수행하기 위한 과업
 * 이 과업은 적합성 테스트를 수행해 결과를 출력한다.
 * <p/>
 * <pre>
 * 용법:
 * &lt;taskdef name=&quot;execute-fitnesse-tests&quot;
 *     classname-&quot;fitnesse.ant.ExecuteFitnesseTestsTask&quot;
 *     classpathref=&quot;classpath&quot; /&gt;
 * 또는
 * &lt;taskdef classpathref=&quot;classpath&quot; 
 *             resource=&quot;tastks.properties&quot; /&gt;
 * <p/>
 * &lt;execute-fitnesse-tests
 *     suitepage=&quot;FitNesse.SuiteAcceptanceTests&quot;
 *     fitnesseport=&quot;8082&quot;
 *     resultsdir=&quot;${results.dir}&quot;
 *     resultshtmlpage=&quot;fit-results.html&quot;
 *     classpathref=&quot;classpath&quot; /&gt;
 * </pre>
 */

 

13. 전역 정보

- 코드 일부에 주석을 달면서 시스템의 전반적인 정보를 기술한 주석

=> 근처에 있는 코드만 주석을 달아라

 

<예시>

/**
 * 적합성 테스트가 동작하는 포트: 기본값은 <b>8082</b>.
 *
 * @param fitnessePort
 */
public void setFitnessePort(int fitnessePort){
    this.fitnessePort = fitnessePort;
}

-> 주석은 기본 포트 정보를 기술하지만 함수 자체는 포트 기본값을 전혀 통제하지 못한다.

 

14. 너무 많은 정보

- 흥미로운 역사나 관련 없는 정보를 장황하게 늘어놓은 주석

 

<예시>

/*
 RFC 2045 - Multipurpose Internet Mail Extensions (MIME)
 1부: 인터넷 메시지 본체 형식
 6.8절. Base64 내용 전송 인코딩(Content-Transfer-Encoding)
 인코딩 과정은 입력 비트 중 24비트 그룹을 인코딩된 4글자로 구성된
 ...
*/

 

15. 모호한 관계

- 주석과 주석이 설명하는 코드의 사이가 명백하지 않은 주석

 

<예시>

/*
 * 모든 픽셀을 담을 만큼 충분한 배열로 시작한다(여기에 필터 바이트를 더한다).
 * 그리고 헤더 정보를 위해 200바이트를 더한다.
 */
this.pngBytes = new Byte[((this.width + 1) * this.height * 3) + 200];

-> 필터 바이트란 무엇일까? +1과 관련이 있을까? 아니면 *3과 관련이 있을까? 아니면 둘 다?

    한 픽셀이 한 바이트 인가? 200을 추가하는 이유는? ...

-> 주석이 모호하다!

 

16. 함수 헤더

- 짧은 함수는 긴 설명이 필요없음

- 짧고 한 가지만 수행하며 이름을 잘 붙인 함수가 주석으로 헤더를 추가한 함수보다 좋음

 

17. 비공개 코드에서 Javadocs

- 시스템 내부에 속한 클래스와 함수에 생성한 Javadocs

 

<전체적인 예제>

<예시>

/**
 * 이 클래스틑 사용자가 지정한 최대 값까지 소수를 생성한다. 사용된 알고리즘은 에라스토테네스의 체다.
 * <p>
 * 에라스토테네스: 기원전 276년에 리비아 키레네에서 출생, 기원전 194년에 사망
 * ...
 * <p>
 * 알고리즘은 상당히 단순하다. 2에서 시작하는 정수 배열을 대상으로
 * 2의 배수를 모두 제거한다. 다음으로 남은 정수를 찾아 이 정수의 배수를 모두 지운다.
 * 최대 값의 제곱근이 될 때까지 이를 반복한다.
 * 
 * @author Alphonse
 * @version 13 Feb 2002 atp
 */
import java.util.*;

public class GeneratePrimes{
    /**
     * @param maxValue는 소수를 찾아낼 최대 값
     */
    public static int[] generatePrimes(int maxValue){
        if(maxValue >= 2){ // 유일하게 유효한 경우
            // 선언
            int s = maxValue + 1; // 배열 크기
            boolean[] f = new boolean[s];
            int i;
            
            // 배열을 참으로 초기화
            for(i = 0; i < s; i++)
                f[i] = true;
            
            // 소수가 아닌 알려진 숫자를 제거
            f[0] = f[1] = false;
            
            // 체
            int j;
            for(i = 2; i < Math.sqrt(s) + 1; i++){
                if(f[i]){ // i가 남아 있는 숫자라면 이 숫자의 배수를 구한다.
                    for(j = 2 * i; j < s; j += i)
                        f[j] = false; // 배수는 소수가 아니다.
                }
            }
            
            // 소수 개수는?
            int count = 0;
            for(i = 0; i < s; i++){
                if(f[i])
                    count++; // 카운트 증가
            }
            
            int[] primes = new int[count];
            
            // 소수를 결과 배열로 이동한다.
            for(i = 0, j = 0; i < s; i++){
                if(f[i]) // 소수일 경우에
                    primes[j++] = i;
            }
            
            return primes; // 소수를 반환한다.
        }
        else // maxValue < 2
            return new int[0]; // 입력이 잘못되면 비어 있는 배열을 반환한다.
    }
}

 

<개선한 예시>

/**
 * 이 클래스는 사용자가 지정한 최대 값까지 소수를 구한다.
 * 알고리즘은 에라스토테네스의 체다.
 * 2에서 시작하는 정수 배열을 대상으로 작업한다.
 * 처음으로 남아 있는 정수를 찾아 배수를 모두 제거한다.
 * 배열에 더 이상 배수가 없을 때까지 반복한다.
 */

public class PrimeGenerator{
    private static boolean[] crossedOut;
    private static int[] result;
    
    public static int[] generatePrimes(int maxValue){
        if(maxValue < 2)
            return new int[0];
        else{
            uncrossIntegersUpTo(maxValue);
            crossOutMultiples();
            putUncrossedIntegersIntoResult();
            return result;
        }
    }
    
    private static void uncrossIntegersUpTo(int maxValue){
    	crossedOut = new boolean[maxValue + 1];
        for(int i = 2; i < crossedOut.length; i++)
            crossedOut[i] = false;
    }
    
    private static void crossOutMultiples(){
        int limit = determineIterationLimit();
        for(int i = 2; i <= limit; i++)
            if(notCrossed(i))
                crossOutMultiplesOf(i);
    }
    
    private static int determineIterationLimit(){
        // 배열에 있는 모든 배수는 배열 크기의 제곱근보다 작은 소수의 인수다.
        // 따라서 이 제곱근보다 더 큰 숫자의 배수는 제거할 필요가 없다.
        double iterationLimit = Math.sqrt(crossedOut.length);
        return (int) iterationLimit;
    }
    
    private static void crossOutMultiplesOf(int i){
        for(int multiple = 2*i; multiple < crossedOut.length; multiple += i)
            crossedOut[multiple] = true;
    }
    
    private static boolean notCrossed(int i){
        return crossedOut[i] == false;
    }
    
    private static void putUncrossedIntegersIntoResult(){
        result = new int[numberOfUncrossedIntegers()];
        for(int j = 0, i = 2; i < crossedOut.length; i++)
            if(notCrossed(i))
                result[j++] = i;
    }
    
    private static int numberOfUncrossedIntegers(){
        int count = 0;
        for(int i = 2; i < crossedOut.length; i++)
            if(notCrossed(i)
                count++;
        return count;
    }
}

 
 
Clean Code
저자 : 로버트 C. 마틴
출판 : 인사이트
발매 : 2013. 12. 24

'Java > Clean Code' 카테고리의 다른 글

[Clean Code] 4장 - 실습  (0) 2021.09.19
[Clean Code] 3장. 함수 (미완)  (0) 2021.09.11
[Clean Code] 2장 - 실습  (0) 2021.09.02
[Clean Code] 2장. 의미 있는 이름  (0) 2021.08.29
[Clean Code] 1장. 깨끗한 코드  (0) 2021.08.22
Comments