# 第五章 編排
###### tags: `Tag(Clean Code)`
[TOC]
## 大綱
程式碼除了命名與註解以外,編排是很重要的,因為編排是一種溝通方式。然而程式碼的可讀性的因素就是程式的編排,影響可維護和可擴充性,縱使程式碼已被修改成面目全非,程式風格和紀律依然存在。
---
## 垂直的編排
### 概念間的垂直空白區隔
第一種
```
GradientDrawable dateBack = (GradientDrawable) balLayout.getBackground();
dateBack.setColor(Color.parseColor("#008A26"));
GradientDrawable actBack = (GradientDrawable) actLayout.findViewById(R.id.layout).getBackground();
actBack.setColor(Color.parseColor("#008A26"));
GradientDrawable saveBack = (GradientDrawable) saveLayout.findViewById(R.id.layout).getBackground();
saveBack.setColor(Color.parseColor("#40A8B3"));
GradientDrawable debtBack = (GradientDrawable) debtLayout.findViewById(R.id.layout).getBackground();
debtBack.setColor(Color.parseColor("#FAA435"));
GradientDrawable insuranceBack = (GradientDrawable) insuranceLayout.findViewById(R.id.layout).getBackground();
insuranceBack.setColor(Color.parseColor("#FE80B1"));
```
第二種
```
GradientDrawable dateBack = (GradientDrawable) balLayout.getBackground();
dateBack.setColor(Color.parseColor("#008A26"));
GradientDrawable actBack = (GradientDrawable) actLayout.findViewById(R.id.layout).getBackground();
actBack.setColor(Color.parseColor("#008A26"));
GradientDrawable saveBack = (GradientDrawable) saveLayout.findViewById(R.id.layout).getBackground();
saveBack.setColor(Color.parseColor("#40A8B3"));
GradientDrawable debtBack = (GradientDrawable) debtLayout.findViewById(R.id.layout).getBackground();
debtBack.setColor(Color.parseColor("#FAA435"));
GradientDrawable insuranceBack = (GradientDrawable) insuranceLayout.findViewById(R.id.layout).getBackground();
insuranceBack.setColor(Color.parseColor("#FE80B1"));
```
空白行被刪除了,造成程式碼的可讀性明顯失焦了。
每一個空白行代表一個是覺得提示。
---
### 垂直的順序
一個被呼叫的函式,應該要出現在『執行呼叫的函式』的下方。
---
## 水平的編排
一個被呼叫的函式,應該要出現在『執行呼叫的函式』的下方,
相近的概念應放在同一個檔案裡。
* Variable Declarations(變數宣告)
區域變數應該在每個函式的上方進行宣告。
* Instance Variables(實體變數)
* Dependent Functions(相依的涵式)
函式呼叫某個函式,在垂直編排上要盡可能靠近。
* Conceptual Affinity(概念相似性)
---
### 水平的空白間隔和密度
* 設定運算子
增加空白,使其更突出。
```
private void measureLine(String line) {
lineCount++;
int lineSize = line.length();
totalChars += lineSize;
lineWidthHistogram.addLine(lineSize, lineCount);
recordWidestLine(lineSize);
}
```
* 函數和其參數
小左括不會加入空白,會誤解為非相關。
==(2*a)== 乘法因子間沒有空白,有較高的優先權。
```
public class Quadratic {
public static double root1(double a, double b, double c) {
double determinant = determinant(a, b, c);
return (-b + Math.sqrt(determinant)) / (2*a);
}
public static double root2(int a, int b, int c) {
double determinant = determinant(a, b, c);
return (-b - Math.sqrt(determinant)) / (2*a);
}
private static double determinant(double a, double b, double c) {
return b*b - 4*a*c;
}
}
```
### 水平的對齊
* 第一種
這樣的對齊強調了不該注意,讓目光無法看到程式碼真正意圖,不自覺被吸引去由上往下閱讀一連串的變數名稱,忽略了型態。
```
public class FitNesseExpediter implements ResponseSender{
private Socket socket;
private InputStream input;
private OutputStream output;
private Request request;
private Response response;
private FitNesseContext context;
protected long requestParsingTimeLimit;
private long requestProgress;
private long requestParsingDeadline;
private boolean hasError;
public FitNesseExpediter(Socket s,FitNesseContext context) throws Exception {
this.context = context;
socket = s;
input = s.getInputStream();
output = s.getOutputStream();
requestParsingTimeLimit = 10000;
}
}
```
* 第二種
問題會出現在列表的長度(需要被拆開)
```
public class FitNesseExpediter implements ResponseSender{
private Socket socket;
private InputStream input;
private OutputStream output;
private Request request;
private Response response;
private FitNesseContext context;
protected long requestParsingTimeLimit;
private long requestProgress;
private long requestParsingDeadline;
private boolean hasError;
public FitNesseExpediter(Socket s,FitNesseContext context) throws Exception {
this.context = context;
socket = s;
input = s.getInputStream();
output = s.getOutputStream();
requestParsingTimeLimit = 10000;
}
}
```
### 縮排
階層結構而非大鋼結構,沒有縮排的程式碼會造成視覺上的混淆,不易閱讀。
* 沒縮排
```
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 { FitNesseExpediter sender = new
FitNesseExpediter(s, context);
sender.setRequestParsingTimeLimit(requestTimeout); sender.start(); }
catch(Exception e) { e.printStackTrace(); } } }
```
* 有縮排
```
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 {
FitNesseExpediter sender = new FitNesseExpediter(s, context);
sender.setRequestParsingTimeLimit(requestTimeout);
sender.start();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
```