-
Notifications
You must be signed in to change notification settings - Fork 0
5장 형식 맞추기
김윤수 edited this page Aug 1, 2022
·
1 revision
프로그래머라면 형식을 깔끔하게 맞춰 코드를 짜야한다.
코드 형식은 의사소통의 일환이며 전문 개발자의 일차적인 의무다.
- 오늘 구현한 코드의 가독성은 앞으로의 코드에 영향을 끼친다.
- 처음 구현 스타일과 가독성 수준은 유지보수와 확장성에 영향을 끼친다.
- 원래 코드가 사라져도 스타일과 규율은 사라지지 않는다.
코드는 바뀔 가능성이 있지만 코드의 가독성은 계속 영향을 미친다.
- 큰 파일보다 작은 파일이 일반적으로 이해하기 쉽다.
- 200줄 정도인 파일로도 커다란 시스템을 구축할 수 있다.
- 반드시 지킬 엄격한 규칙은 아니지만 바람직한 규칙으로 삼자.
소스 파일도 신문과 비슷하게 작성한다.
- 소스 파일의 첫부분은 고차원 개념과 알고리즘을 설명한다.
- 아래로 내려갈수록 의도를 세세하게 묘사한다.
- 마지막에는 가장 저차원 함수와 세부 내역이 나온다.
거의 모든 코드는 왼쪽에서 오른쪽으로 위에서 아래로 읽는다.
- 각 행은 수식이나 절을 나타낸다.
- 일련의 행 묶음은 완결된 생각 하나를 표현한다.
- 생각 사이는 빈 행을 넣어 분리해야 마땅하다.
package a
public class aa {
public static final String b;
private static final C c;
public aa() {};
public String d() {};
}
빈 행을 빼버리면 가독성이 현저하게 떨어진다.
package a
public class aa {
public static final String b;
private static final C c;
public aa() {};
public String d() {};
}
줄 바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미한다.
즉, 서로 밀접한 코드 행은 세로로 가까이 놓여야 한다는 뜻이다.
public class ReporterConfig {
/**
* 리포터 리스너의 클래스 이름
*/
private String m_className;
/**
* 리포터 리스너의 속성
*/
private List<Property> m_properties = new ArrayList<Property>();
public void addProperty(Property property) {
m_properties.add(property);
}
}
변수 2개 메소드 1개가 있지만 떨어져있어 한눈에 들어오지 않는다.
public class ReporterConfig {
private String m_className;
private List<Property> m_properties = new ArrayList<Property>();
public void addProperty(Property property) {
m_properties.add(property);
}
}
서로 밀접한 개념은 가까이 둬야 한다.
- 밀접한 두 개념은 세로 거리로 연관성을 표현한다.
- 두 개념이 떨어져 있으면 읽는 사람이 여기저기 뒤지게 된다.
- 변수는 사용하는 위치에 최대한 가까이 선언한다.
private void method1() {
InputStream is = null;
try{
...
} catch {
...
}
}
- 루프를 제어하는 변수는 흔히 루프 문 내부에 선언한다.
public int countTestCases() {
int count = 0;
for (Test each : tests)
count += each.countTestCases();
return count;
}
- 드물지만 긴 함수에서 블록 상단이나 루프 직전에 변수를 선언하는 경우도 있다.
for(XmlTest test : m_suite.getTests()) {
TestRunner tr = m_runnerFactory.newTestRunner(this, test);
...
}
- 인스턴스 변수는 클래스 맨 처음에 선언한다.
- 변수 간에 세로로 거리를 두지 않는다.
public class A {
private String a;
private String b;
private String c;
...
}
- 한 함수가 다른 함수를 호출한다면 두 함수는 세로로 가까이 배치한다.
- 가능하다면 호출하는 함수를 먼저 배치한다.
public class A {
private String a;
public void method1() {
a = method2();
}
public String method2() {
return "A.a";
}
...
}
개념적 친화도가 높을수록 가까이 배치한다.
- 한 함수가 다른 함수를 호출해 생기는 직접적인 종속성
- 변수와 그변수를 사용하는 함수의 종속성
- 비슷한 동작을 수행하는 일군의 함수의 종속성
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);
}
...
}
- 명명법이 똑같고 기본 기능이 유사하고 간단하다.
- 종족적 관계가 없더라도 가까이 배치할 함수들이다.
- 일반적으로 함수 호출 종속성은 아래 방향으로 유지한다.
- 함수를 호출하는 함수보다 나중에 배치한다
- 소스코드 모듈이 고차원에서 저차원으로 자연스럽게 내려간다.
- 가장 중요한 개념을 가장 먼저 표현한다.
프로그래머는 짧은 행을 선호한다.
- 짧은 행이 바람직하다.
- 80자 제한은 인위적이다. 100자나 120자도 나쁘지 않다.
- 개인적으로 120자 정도를 권장한다.
가로로는 공백을 사용해 밀접한 개념과 느슨한 개념을 표현한다.
private void measureLine(String line) {
lineCount++;
int lineSize = line.length();
totalChars += lineSize;
lineWidthHistogram.addLine(lineSize, lineCount);
recordWidfestLine(lineSize);
}
- 할당 연산자
int lineSize = line.length();
- 강조하려 앞뒤에 공백을 줬다.
- 공백을 넣으면 두 가지 주요 요소가 확실히 나뉜다.
- 함수 이름
lineWidthHistogram.addLine(lineSize, lineCount);
- 이름과 괄호 사이에 공백을 넣지 않았다.
- 함수와 인수는 서로 밀접하기 때문이다. (넣으면 별개로 보인다)
- 괄호 안 인수는 공백으로 분리해 별개라는 사실을 보여준다.
(lineSize, lineCount)
- 연산자 우선순위
(-b - Math.sqrt(determinant)) / (2*a)
- 승수 사이에는 공백이 없다. (우선순위가 가장 높음)
- 항 사이에는 공백이 들어간다. (덧셈 뺄셈은 우선순위가 낮음)
특정 구조를 강조하기 위해 가로 정렬을 사용했다.
public class FitNesseExpediter
{
private Socket socket;
private InputStream input;
private Outputstream output;
protected long requestParsingTimeLimit;
private long requestProgress;
...
public FitNesseExpediter(Socket s,
FitNesseContext context) throws Exception
{
socket = s;
input = s.getInputStream();
output = s.getOutputStream();
}
}
- 이는 유용하지 않다.
- 변수 유형은 무시하고 변수 이름부터 읽게 된다.
정렬이 필요할 정도로 목록이 길다면 문제는 목록 길이지 정렬 부족이 아니다.
- 소스 파일은 유곽도와 계층이 비슷하다.
- 범위로 이뤄진 계층을 표현하기 위해 우리는 코드를 들여쓴다.
프로그래머는 들여쓰기 체계에 크게 의존한다.
public class FitNesseServer implements SocketServer { private FitNesseContext context; public FitNesseserver(FitNesseContext context) { this.context = context; }public void serve(Socket s) { serve(s, 10000); } public void serve(Socket s, long requestTimeout) { try { ... } catch { ... }}}
들여쓰지 않는다면 코드를 알아 볼 수 없다.
public class FitNesseServer implements SocketServer {
private FitNesseContext context;
public FitNesseserver(FitNesseContext context) {
this.context = context;
}
public void serve(Socket s) {
serve(s, 10000);
}
public void serve(Socket s, long requestTimeout) {
try {
...
}
catch {
...
}
}
}
빈 while문이나 for문은 새 행에다 들여써서 ;을 넣어준다.
while (dis.read(buf, 0, readBufferSize) != -1)
;
프로그래머라면 각자 선호하는 규칙이 있다. 하지만 팀에 속한다면 팀 규칙을 따라야한다.
- 팀은 한가지 규칙에 합의해야 하고 그 규칙을 따라야한다.
- 소프트웨어 시스템은 읽기 쉬운 문서로 이뤄지고 스타일은 일관적이고 매끄러워야 한다.
- 한 소스 파일에서 봤던 형식이 다른 소스파일에도 쓰이리라는 신뢰감을 주어야한다.
⏳ 2회차(7.17) - 2, 3장
⏳ 3회차(7.31) - 4, 5장
⏳ 4회차(8.14) - 6, 7장
⏳ 5회차(8.28) - 8, 9장
⏳ 6회차(9.11) - 10, 11장
⏳ 7회차(9.25) - 12, 13장
⏳ 8회차(9.25) - 14, 15장
⏳ 9회차(9.25) - 16, 17장