# clean code
https://medium.com/trell-labs/refactoring-for-clean-and-extensible-codebase-ee746040fddf
https://github.com/jincheng9/practical-go-cn
## If-Else 階梯

添加我們的日誌記錄和錯誤處理,它看起來像這樣。

這些“過濾器”分組在同一數據類型下。這樣,我們可以將它們存儲在一個數組中,然後循環遍歷它們。

我們在這裡並不特別需要接口。我們也可以通過在 Golang 中定義自定義類型來做到這一點。像這樣的東西

但是通過這樣做,我們失去了Name()在記錄時非常有用的方法
**重構!!**


## screaming Architecture https://medium.com/kuma%E8%80%81%E5%B8%AB%E7%9A%84%E8%BB%9F%E9%AB%94%E5%B7%A5%E7%A8%8B%E6%95%99%E5%AE%A4/%E8%AE%93%E7%B5%90%E6%A7%8B%E5%B0%96%E5%8F%AB%E5%90%A7-%E8%A4%87%E7%BF%92-clean-code-%E6%9C%89%E6%84%9F-f1d918e2a568
https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html
## The Great Wall
a+b+c但將來可能會更改為a*b/c

存儲這些值的更好方法是在 HashMap 中。這看起來像這樣score = map["weightA"] + map["weightB"] + map["weightC"]。
在執行此類操作時,我不太喜歡使用原始字符串。有多種原因,我不會在這裡深入探討。我將展示一種稍微好一點的方法來實現這一點,而無需使用字符串。
**重構**


因為我不想在我的 HashMap 中使用原始字符串,所以我將不得不定義一個我可以使用的不同數據類型。
最終產品

## 可重複使用的管道
它從一個數據源獲取數據,對其進行處理並將其放入另一個數據源。當我們必須同時從多個來源獲取數據時,就會出現混亂。並且根據我們從哪個數據源獲取數據,我們必須對其進行略微不同的處理,具體是查詢數據庫中的不同表。
我將通過一個例子更詳細地解釋這種情況。
我們的一項子服務具有三個不同的“管道”。
MT5
DTOD
YT
在所有這些流水線中,我們所做的工作都是一樣的。從數據庫獲取事件,處理它們,將它們保存到另一個數據庫中。唯一的區別是我們必須為所有三個管道使用不同的表。

由於我們有多個管道要使用,我們需要修改這段代碼來處理它們

重構
解決方案再次在於接口

類似於某種“策略模式”
最終


第二階段
fetchEvents()正在調用ProcessEvents()處理事件。那麼……它如何知道需要將哪個存儲庫傳遞給它。它必須知道它正在處理哪個管道並創建它。正確的?(debug的時候要怎知道是哪個錯誤)
定義的接口

我們用來獲取Fetcher和的工廠方法Processor。

fetcher從內部看起來的樣子

最終

## 自訂struct 處理重複
https://github.com/jincheng9/practical-go-cn#762-writeresponse
old
```
type Header struct {
Key, Value string
}
type Status struct {
Code int
Reason string
}
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
_, err := fmt.Fprintf(w, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
if err != nil {
return err
}
for _, h := range headers {
_, err := fmt.Fprintf(w, "%s: %s\r\n", h.Key, h.Value)
if err != nil {
return err
}
}
if _, err := fmt.Fprint(w, "\r\n"); err != nil {
return err
}
_, err = io.Copy(w, body)
return err
}
```
首先,我們使用 構建狀態行fmt.Fprintf,並檢查錯誤。然後為每個標頭我們編寫標頭鍵和值,每次檢查錯誤。最後,我們用一個額外的 終止標頭部分\r\n,檢查錯誤,並將響應正文複製到客戶端。最後,雖然我們不需要檢查錯誤 from io.Copy,但我們需要將其從返回的兩個返回值形式io.Copy轉換為返回的單個返回值WriteResponse。
這是很多重複性的工作。但是我們可以通過引入一個小的包裝器類型來使我們自己更容易errWriter。
errWriter履行io.Writer合同,因此它可用於包裝現有的io.Writer. errWriter將寫入傳遞給其底層編寫器,直到檢測到錯誤。從那時起,它會丟棄所有寫入並返回先前的錯誤。
new
```
type errWriter struct {
io.Writer
err error
}
func (e *errWriter) Write(buf []byte) (int, error) {
if e.err != nil {
return 0, e.err
}
var n int
n, e.err = e.Writer.Write(buf)
return n, nil
}
func WriteResponse(w io.Writer, st Status, headers []Header, body io.Reader) error {
ew := &errWriter{Writer: w}
fmt.Fprintf(ew, "HTTP/1.1 %d %s\r\n", st.Code, st.Reason)
for _, h := range headers {
fmt.Fprintf(ew, "%s: %s\r\n", h.Key, h.Value)
}
fmt.Fprint(ew, "\r\n")
io.Copy(ew, body)
return ew.err
}
```
應用errWriter到WriteResponse極大地提高了代碼的清晰度。每個操作不再需要用錯誤檢查來括起來。通過檢查字段,報告錯誤被移動到函數的末尾ew.err,避免了 `io.Copy 返回值的煩人翻譯。
## 只處理一次錯誤
只處理一次錯誤
https://github.com/jincheng9/practical-go-cn#77-only-handle-an-error-once
###### tags: `Go`