본문 바로가기
IT 공부/Clean Code

Clean Code 요약해보기 (4)

by 수박한암살자 2022. 5. 1.

오랜만에 클린코드 다시 읽는다.

그동안 공부를 안한 건 아니지만 습관이 역시 끊기면 이렇게 된다 ㅠㅠ

 

5장부터 다시 스타트.

 


5. 형식 맞추기

형식을 맞추는 목적

- 코드 형식은 의사소통의 일환이다. 의사소통은 전문 개발자의 일차적인 의무다.

 

적절한 행 길이를 유지하라

- JUnit, FitNess, Time and Money, testNG, JDepend, Ant, Tomcat 프로젝트들을 서로 비교해보면 코드 줄이 적어도 커다란 시스템을 구축할 수 있다는 사실을 알 수 있다. 반드시 그럴 필요는 없지만 줄여보도록 노력하자.

 

신문 기사처럼 작성하라

- 이름은 간단하면서도 설명이 가능하게 짓는다. 이름만 보고도 올바른 모듈을 살펴보고 있는지 아닌지를 판단할 정도로 신경 써서 짓는다. 소스 파일 첫 부분은 고차원 개념과 알고리즘을 설명한다. 아래로 내려갈수록 의도를 세세하게 묘사한다. 마지막에는 가장 저차원 함수와 세부 내역이 나온다.

 

개념은 빈 행으로 분리하라

- 예제 코드와 함께 보자. 행이 없으면 코드 가독성이 현저하게 떨어진다.

 

package fitnesse.wikitext.widgets;

import java.util.regex.*;

public class BoldWidget extends ParentWidget {
    public static final String REGEXP = "'''.+?'''";
    private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
      Pattern.MULTILINE + Pattern.DOTALL
    );
    
    public BoldWidget(ParentWidget parent, String text) throws Exception {
        super(parent);
        Matcher match = pattern.matcher(text);
        match.find();
        addChildWidgets(match.group(1));
    }
    
    public String render() throws Exception {
        StringBuffer html = new StringBuffer("<b>");
        html.append(childHtml()).append("</b>");
        return html.toString();
    }
}


// 밑과 비교해보자

package fitnesse.wikitext.widgets;
import java.util.regex.*;
public class BoldWidget extends ParentWidget {
    public static final String REGEXP = "'''.+?'''";
    private static final Pattern pattern = Pattern.compile("'''(.+?)'''",
      Pattern.MULTILINE + Pattern.DOTALL
    );
    public BoldWidget(ParentWidget parent, String text) throws Exception {
        super(parent);
        Matcher match = pattern.matcher(text);
        match.find();
        addChildWidgets(match.group(1));
    }   
    public String render() throws Exception {
        StringBuffer html = new StringBuffer("<b>");
        html.append(childHtml()).append("</b>");
        return html.toString();
    }
}

 

세로 밀집도

- 줄바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미한다. 즉, 서로 밀집한 코드 행은 세로로 가까이 놓여야 한다는 뜻이다.

public class ReporterConfig {
    /**
     * 리포터 리스너의 클래스 이름
     */
    private String m_className;
    
    /**
     * 리포터 리스너의 속성
     */
    private List<Property> m_properties = new ArrayList<Property>();
    public void addProperty(Property property) {
        m_properties.add(property);
    }
}


// 세로를 가깝게 해주자

public class ReporterConfig {
    private String m_className;
    private List<Property> m_properties = new ArrayList<Property>();
    public void addProperty(Property property) {
        m_properties.add(property);
    }
}

 

수직 거리

- 코드를 이해하려고 여기저기 오갔는데 결국 혼란만 가중된 경험이 있는가? 결코 달갑지 않다. 서로 밀접한 개념은 세로로 가까이 둬야 한다. 연관성이 깊은 두 개념이 멀리 떨어져 있으면 코드를 읽는 사람이 소스 파일과 클래스를 여기저기 뒤지게 된다.

변수는 사용하는 위치에 최대한 가까이 선언한다.

인스턴스 변수는 클래스 맨 처음에 선언한다. 하지만 논쟁이 분분하기도 하다.

종속 함수는 서로 세로로 가까이 배치한다. 또한 가능하다면 호출하는 함수를 호출되는 함수보다 먼저 배치한다. 

개념적으로 유사한 경우도 서로 가까이 배치한다. 이 경우는 아래 코드를 보자.

public class Assert {
    static public void assertTrue(String message, boolean condition) {
        if (!condition)
            fail(message);
    }
    
    static public void assertTrue(boolean condition) {
        assertTrue(null, condition);
    }
    
    static public void assertFalse(String message, boolean condition) {
        assertTrue(message, !condition);
    }
    
    static public void assertFalse(boolean condition) {
        assertFalse(null, condition);
    }
...생략

위 함수들은 개념적인 친화도가 매우 높다. 명명법이 똑같고 기본 기능이 유사하고 간단하다. 서로가 서로를 호출하는 관계는 부차적인 요인이다. 종속적인 관계가 없더라도 가까이 배치할 함수들이다.

 

가로 형식 맞추기

- 가로도 짧은 행이 바람직하다. 100~120자 정도면 적당하다고 책에서는 말하고 있다. (비록 모니터가 크더라도...)

 

가로 공백과 밀집도

- 가로로는 공백을 사용해 밀접한 개념과 느슨한 개념을 표현한다. 아래 함수를 보자.

private void measureLine(String line) {
    lineCount++;
    int lineSize = line.length();
    totalChars += lineSize;
    lineWidthHistogram.addLine(lineSize, lineCount);
    recordWidestLine(lineSize):
}

할당 연산자를 강조하려고 앞뒤에 공백을 줬다. 할당문은 왼쪽 요소와 오른쪽 요소가 분명히 나뉜다. 공백을 넣으면 두 가지 요소가 확실히 나뉜다는 사실이 더욱 분명해진다.

 

반면, 함수 이름과 이어지는 괄호 사이에는 공백을 넣지 않았다. 함수와 인수는 서로 밀접하기 때문이다.

 

연산자 우선순위를 강조하기 위해서도 공백을 사용한다. 

 

가로 정렬

- 코드를 먼저 보도록 하자.

public class FitNessExpediter implements ResponseSender
{
    private   Socket               socket;
    private   InputStream          input;
    private   OutputStream         output;
    private   Request              request;
    ...
}

이런 코드는 유용하지 못하다. 코드가 엉뚱한 부분을 강조해 진짜 의도가 가려지기 때문이다. 예를 들어, 위 선언부를 읽다 보면 변수 유형은 무시하고 변수 이름부터 읽게 된다.

 

들여쓰기 무시하기

- 때로는 들여쓰기를 안하고 싶기도 하다. 하지만 한 행에 다 작성하기 보다는 펼치는 것이 났다.

public class CommentWidget extends TextWidget
{
    public static final String REGEXP = "^#[^\r\n]*(?:(?:\r\n)|\n|\r)?";
    
    public CommentWidget(ParentWidget parent, String text){super(parent, text);}
    public String render() throws Exception {return ""; }
}

// 들여쓰기를 적용하자.

public class CommentWidget extends TextWidget {
    public static final String REGEXP = "^#[^\r\n]*(?:(?:\r\n)|\n|\r)?";
    
    public CommentWidget(ParentWidget parent, String text) {
        super(parent, text);
    }
    
    public String render() throws Exception {
        return "";
    }
}

 

가짜 범위

- 때로는 빈 while 문이나 for 문을 접한다. 이런 경우 빈 블록을 올바로 들여쓰고 괄호로 감싼다. 그리고 세미콜론은 세 행에다 제대로 들여써서 넣어준다.

while (dis.read(buf, 0, readBufferSize) != -1)
;

 

팀 규칙

- 팀은 한 가지 규칙에 합의해야 한다. 그리고 모든 팀원은 그 규칙을 따라야 한다. 

반응형

'IT 공부 > Clean Code' 카테고리의 다른 글

Clean Code 요약해보기 (6)  (0) 2022.05.18
Clean Code 요약해보기 (5)  (0) 2022.05.11
Clean Code 요약해보기 (3)  (0) 2022.03.16
Clean Code 요약해보기 (2)  (0) 2022.03.14
Clean Code 요약해보기 (1)  (0) 2022.03.08

댓글