Refactoring Command
指令套件 github.com/urfave/cli
算蠻好上手的。雖然好用,但似乎其他套件也不錯,如 Cobra 等。
目前 Command 實際處理任務的程式都是直接依賴 cli.Context
,這樣會違反依賴反轉原則--應該依賴更抽象的參數,如 Predeclared Type 或 interface 等。
會發現這個問題是因為在實作 HTTP Server 時,行為沒變,只有輸出改變而已,但卻必須要為 /generate
的回傳重新客製化寫法,而且這段程式碼與 command/generate.go
裡面的 generate
函式太像了,這是一個明顯的壞味道,必須要重新調整設計才行。
分析
首先先調整 command/generate.go
的 generate
函式。它的任務是產生一堆假資料,所以我們應該可以先把數量這個參數先抽離出來:
func generate(count int, c *cli.Context) error { |
接著取得 res
是 provider 的任務,因此它不適合離開這個範圍,但取得檔案名稱的 c.GlobalString("provider")
,是可以抽離的:
func generate(path string, count int) error { |
最後就是 fmt.Println(generator.Name())
這是 cli 專屬的行為,因此把它抽出變成 Closure,同時把這個方法也公開:
type GenerateItemProcess func(item string) |
到目前為止,只要把上面的程式碼找個地方放即可。因為這有點類似 Facade Pattern,因此決定放在 facade/facade.go
裡。原本 CLI 呼叫的地方改成這樣:
func(c *cli.Context) error { |
同時把不必要的程式碼刪除,import 的項目就不需要 provider
了,改成依賴 facade
。HTTP Server 實作的部分也可以如法炮製:
func serve(c *cli.Context) error { |
同樣的重構方法也可以用在 QueryCommand
,後面就不贅述了。
另外兩個 Command:
ServeCommand
目前不知道該怎麼拆出來好(因為裡面也有用到 Facade);StatusCommand
則是因為太簡單,所以沒拆出來的必要。
效能改善
以上已經把 Generate
的任務解耦合了,但 HTTP Server 的效能上是有問題的,當 num 到了一個極大的數如 10,000,000,執行會需要花 7 秒:
[GIN] 2018/01/01 - 23:37:19 | 200 | 7.610408045s | 127.0.0.1 | GET /generate |
主要是因為 append
了一千萬次,我們把 Generate
程式改一下:
type GenerateItemProcess func(item string, index int) |
讓 process 可以順便把目前處理的 index 也傳入 Closure,HTTP Server 可以改寫成這樣:
s := make([]string, num) |
這樣算是用空間換時間,時間結果約為原本的 65%:
[GIN] 2018/01/01 - 23:41:34 | 200 | 4.963947486s | 127.0.0.1 | GET /generate |
程式碼修改可以參考 PR Day 27
問題
Interface 沒認真去了解,還真不知道該怎麼做好。
明天把 Command 的參數加完後,後天就來研究 Interface !
參考資料
- 開發者能察覺的壞味道(Bad Smell) - 看到 code 寫成這樣我也是醉了,不如試試重構?
- SOLID 之 依賴反轉原則(Dependency inversion principle) - 看到 code 寫成這樣我也是醉了,不如試試重構?
- Facade 模式 - 良葛格學習筆記