---
# System prepended metadata

title: 第五章 編排
tags: [Tag(Clean Code)]

---

# 第五章 編排

###### 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();
        }
     }
}
```