- vừa được xem lúc

[GOLANG] - Áp dụng D trong nguyên tắc SOLID với Google Wire

0 0 18

Người đăng: Quoc Huy Tran

Theo Viblo Asia

1. D trong SOLID

Nếu là 1 developer đã từng tiếp nhận nhiều source code khác nhau, thì việc đọc những code cũ là 1 công việc vô cùng đau đầu bởi rõ ràng không phải developer nào cũng "siêng" ghi lại document cũng như viết mã code thật sự "clean". Chưa kể việc viết thêm những mã code phục vụ tính năng mới trong những source code lâu đời và dirty cũng gây ra không ít khó khăn. Ví dụ như là code thêm tính năng nhưng lại ảnh hưởng tính năng cũ, hay đụng phải những technial dept mà những developer "hào phóng" trước đó để lại. SOLID là nguyên tắc là đa phần developer hướng tới và áp dụng, những nguyên tắc trong SOLID giúp cho việc mở rộng, phát triển code dễ dàng hơn. Bảo trì code cũng như giúp những người mới có thể đọc code dễ dàng hơn. D là Dependecies Injection và nội dung trong đó chủ yếu:

  • Các module cấp cao không nên phụ thuộc vào các modules cấp thấp.
  • Cả 2 nên phụ thuộc vào abstraction.
  • Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại ( Các class giao tiếp với nhau thông qua interface, không phải thông qua implementation.)

2. Áp dụng Dependecies Injection với Wire trong Golang

Trong golang, có các thư viện hỗ trợ cho dependencies injection khá tốt và trong số đó, thư viện mình dùng khá quen thuộc là google-wire. Google-wire là cơ chế tự động generate các mã code nhúng các interface đã khai báo mà chúng ta không cần chủ động, việc ta quan tâm là đưa các interface vào, và wire sẽ làm phần việc còn lại.

Link google-wire: https://github.com/google/wire

Sau đây mình sẽ code ví dụ, ở các ví dụ sau mình sẽ setup 1 server api , 1 server consumer có dùng wire Đầu tiên ở terminal ứng dụng golang , chạy lệnh go install github.com/google/wire/cmd/wire@latest để cài đặt thư viện wire vô máy Gõ wire để kiểm tra cài đặt thành công hay chưa ở terminal Ở ứng dụng api này, mình sẽ dùng labstack-echo để làm server http api Đầu tiên mình sẽ khởi tạo HealthService. Ở health.go, mình khai báo interface cho HealthService

type IHealthService interface { GetHealth() string
}

Tạo HealthService và implement IHealthService

type healthService struct {
} func (h healthService) GetHealth() string { return "Health"
}

Viết hàm khởi tạo HealthService

func NewHealthService() IHealthService { return &healthService{}
}

Như vậy mình đã xong việc khai báo cũng như Interface của HealthService Vậy file code đầy đủ của HealthService sẽ là

type IHealthService interface { GetHealth() string
} type healthService struct {
} func (h healthService) GetHealth() string { return "Health"
} func NewHealthService() IHealthService { return &healthService{}
}

Ở file main.go, mình sẽ thực hiện việc khai báo ứng dụng app api và thực hiện Inject cho HealthService

type ApiApplication struct { IHealthService service.IHealthService
} func NewApiApplication(IHealthService service.IHealthService) ApiApplication { return ApiApplication{IHealthService: IHealthService}
} func (api ApiApplication) RunApp() { e := echo.New() e.GET("/", func(c echo.Context) error { response := api.IHealthService.GetHealth() return c.String(http.StatusOK, response) }) e.Logger.Fatal(e.Start(":1323"))
}

Ở ví dụ trên, mình đã thực hiện khởi tạo ứng dụng API cũng như inject HealthService và phục vụ cho 1 api, ta có thể thấy, ở đây ApiApplication chỉ đang Inject đúng interface của HealthService. Vậy hàm NewApiApplication sẽ dùng ở đâu? Tiếp theo mình sẽ tạo 1 file wire.go và thực hiện Inject các interface vào

func InitApplication() (ApiApplication, func()) { wire.Build( NewHealthService, NewApiApplication, ) return ApiApplication{}, func() { }
}

Nói rõ hơn thì mọi interface mà mình muốn Inject vào đều sẽ khai báo khởi tạo new ở đây. Sau đó mình chạy lệnh wire ./... Khi chạy lệnh này, thư việc wire sẽ rà soát toàn bộ khai báo interface ở thư mục wire.go, tạo ra 1 file mới là wire_gen.go, trong thư mục wire_gen.go sẽ chưa toàn bộ code inject vào. wire_gen.go

func InitApplication() (ApiApplication, func()) { iHealthService := service.NewHealthService(iCassandraProvider) apiApplication := NewApiApplication(iHealthService) return apiApplication, func() { }
}

tiếp theo để start app thì ở func main() sẽ code

app,cleanup := InitApplication()
defer cleanUp()
app.RunApp()

Giải thích về function cleanUp khi trả về sẽ chứa các func mà ta muốn thực hiện sau khi đóng app, ví dụ các hàm close database, warning. Sau này khi thực hiện 1 inject khác, chúng ta chỉ cần chạy lệnh wire./... để thực hiện generate ra 1 file wire_gen.go mới là xong, ta không cần chủ động thực inject gì khác. Nếu có 1 interface mới cần inject thì vô file wire.go để khai báo và chạy wire ./...

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Regular Expression và cuộc đời bạn sẽ bớt khổ (Updated v2.2)

. Regular Expression (RegEx) à? Nghe quen quen. . Bạn cần xử lý validate (kiểm tra tính hợp lệ) các trường dữ liệu nhập vào ô Text. .

0 0 109

- vừa được xem lúc

Naming rules - Các quy tắc vàng trong làng đặt tên

. . Đã bao giờ bạn gặp khó khăn khi phải suy nghĩ nên đặt tên biến/hàm như thế nào trong lúc code.

0 0 34

- vừa được xem lúc

Tóm tắt cuốn Clean Code của Uncle Bob

Bài viết được dịch từ Gist của wojteklu. . Quy tắc chung. .

0 0 103

- vừa được xem lúc

Tôi đi học code - lớp mẫu giáo

Chào mừng các bạn quay trở lại với blog duthaho.com.

0 0 60

- vừa được xem lúc

Lean Code CSS

Khi thiết kế và phát triển web, đôi lúc chúng ta gặp khó khăn trong việc tổ chức và quản lý code CSS. Nhiều nhà thiết kế website nghĩ rằng việc tổ chức và quản lý code thật là rắc rối, tuy nhiên nếu b

0 0 32

- vừa được xem lúc

Design Patterns là gì? Tại sao nó lại là trợ thủ đắc lực của Developers

Design Pattern là một giải pháp chung để giải quyết các vấn đề phổ biến khi thiết kế phần mềm trong lập trình hướng đối tượng OOP. Design pattern là các giải pháp tổng thể đã được tối ưu hóa, được tái

0 0 60