자바 코딩을 하면서 여러가지 패턴을 적용하고, 인터페이스를 이용하는 등 멋지게 하고 싶어한다. 하지만, 그 전에 가장 기초적인게 코드의 형식을 맞추는 것이라 생각한다. IDE에서 Formatter 를 적용해 놓으면 팀내 들여쓰기 등 형식은 맞출 수 있다. 하지만 클래스의 세로 길이, 메소드의 세로 길이는 어느 정도가 적합할까?
내가 만드는 기능이 복잡해서 클래스가 크다고? JUnit과 같은 큰 프로젝트도 대부분 200줄 정도의 파일로 구성되어 있다. 즉, 방금과 같은 변명은 핑계이다.
신문 기사처럼 작성하라
아주 좋은 신문 기사를 떠올려보라 .
- 독자는 위에서 아래로 기사를 읽음
- 첫 문단은 요약된 내용이다. 세세한 내용은 밑에서 나온다. -> 소스 파일 첫부분은 고차원 개념
- 기사는 짧다
개념은 빈행으로 분리하라
package fitnesse.wikitext.widgets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.Matcher;
import java.util.regex.Pattern;
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);
}
수직 거리
연관성이 깊은 두 개념이 멀리 떨어져 있으면 코드를 읽는 사람이 여기저기를 뒤져야 함
1. 변수 선언
- 변수를 사용하는 위치에 최대한 가까이 선언
- 지역변수는 각 함수 맨 처음에 선언 ( 왜냐하면 우린 함수를 짧게 짧거다 )
private static void readPreferences() {
InputStream is = null;
try {
is = new FileInputStream(getPreferencesFile());
setPreferences(new Properties(getPreferences()));
getPreferences().load(is);
} catch (IOException e) {
try {
if (is != null)
is.close();
} catch (IOException e1) {
}
}
}
2. 인스턴스 변수
- 이건 너무 당연해서 딱히.... 자바라면 클래스 맨위에 변수를 선언해둬야 한다.
3. 종속함수
- 아까 말한 기사처럼 고수준 -> 저수준으로 짤 때, 함수도 같은 순서대로 선언하는 것이다. 그렇게 되면 당연히 위에서 아래로 읽혀지기 때문에 가독성이 좋아진다.
public class WikiPageResponder implements SecureResponder {
protected WikiPage page;
protected PageData pageData;
protected String pageTitle;
protected Request request;
protected PageCrawler crawler;
public Response makeResponse(FitNesseContext context, Request request) throws Exception {
String pageName = getPageNameOrDefault(request, "FrontPage");
loadPage(pageName, context);
if (page == null)
return notFoundResponse(context, request);
else
return makePageResponse(context);
}
private String getPageNameOrDefault(Request request, String defaultPageName) {
String pageName = request.getResource();
if (StringUtil.isBlank(pageName))
pageName = defaultPageName;
return pageName;
}
protected void loadPage(String resource, FitNesseContext context)
throws Exception {
WikiPagePath path = PathParser.parse(resource);
crawler = context.root.getPageCrawler();
crawler.setDeadEndStrategy(new VirtualEnabledPageCrawler());
page = crawler.getPage(context.root, path);
if (page != null)
pageData = page.getData();
}
private Response notFoundResponse(FitNesseContext context, Request request)
throws Exception {
return new NotFoundResponder().makeResponse(context, request);
}
private SimpleResponse makePageResponse(FitNesseContext context)
throws Exception {
pageTitle = PathParser.render(crawler.getFullPath(page));
String html = makeHtml(context);
SimpleResponse response = new SimpleResponse();
response.setMaxAge(0);
response.setContent(html);
return response;
}
...
4. 개념적 유사성
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);
}
가로 들여쓰기 / 가로 밀집도
가로에 관련된 부분은 IDE에 설정된 Formatter에 의해 정해지기 때문에 공부하지 않아도 될 것 같다.
'Clean code' 카테고리의 다른 글
Clean Code 3장 - 함수 (0) | 2020.01.11 |
---|---|
Clean Code 4장 - 주석 (0) | 2020.01.07 |
리팩토링 - 12장. 복합 리팩토링 ( 절차코드를 객체로 전환 ) (0) | 2020.01.05 |
Clean Code 9장 - 단위테스트 (0) | 2018.09.18 |
Clean Code 12장 - 우수한 설계를 위한 규칙(창발성) (0) | 2018.09.13 |