Intro
- Go code được tổ chức theo các package. Trong cùng package, code có thể gọi tới nhau thoải mái, còn ngoài package thì những cháu nào export mới gọi được nhau.
- Tên package luôn nằm phía trước identifier, VD: muốn gọi tới Bar trong package foo, dùng
foo.Bar
- Package name tốt thì giúp code ngon hơn.
- Tên package thường mô tả context package dùng để làm gì, giúp client dễ sử dụng hơn.
- Tên package cũng giúp maintainer dễ dàng xác định một identifier mới thuộc package nào (Kiểu thêm tính năng mới, tên giúp dễ định hình là thêm vào đâu hơn)
- Effective Go cũng cung cấp cách đặt tên package cho xịn. Bài này nói về cách package name tệ, và cách để fix chúng.
Package names
- Package name tốt thì ngắn và rõ ràng.
- lowercase.
- không phải under_score
- không phải mixedCaps
- Thường đơn giản ntn:
time
list
http
- Package cũng có thể được viết tắt các tiền tố:
strconv
syscall
fmt
- Tuy nhiên, nếu viết tắt làm cho khó hiểu hoặc gây hiểu lầm ~> PLEASE DON’T DO IT.
Naming package context
- Package name và content của chúng thường liên quan đến nhau. Khi thiết kế package, hãy đứng từ góc nhìn của người sử dụng.
- Tránh việc lặp lại:
- package
http
rồi ~> chỉ cần đặtServer
thôi, không cần đặtHttpServer
(lúc gọi dùnghttp.Server
, nghe cũng sáng phết rồi).
- package
- Thường kiểu dữ liệu chính của package đặt theo convention:
pkg.Pkg
- VD:
time.Time
- VD:
- Dùng hàm
New()
sẽ trả về kiểu dữ liệu củapkg.Pkg
- VD:
q := list.New()
return kiểu*list.List
- VD:
- Thường nếu function trả về kiểu dữ liệu không trùng với tên package, thì trong tên function sẽ chứa kiểu dữ liệu ấy. VD:
d, err := time.ParseDuration("10s") // d có kiểu time.Duration
timer := time.NewTimer(d) // timer có kiểu `*time.Timer`
- Các kiểu dữ liệu ở các package khác nhau có thể trùng tên, VD:
jpeg.Reader
,bufio.Reader
,… Việc này chả có vấn đề gì, vì có tên package phía trước rồi.
Package path
- Package có thể chứa cả tên và path
import (
"context" // package context
"fmt" // package fmt
"golang.org/x/time/rate" // package rate
"os/exec" // package exec
)
- Với package path thì phần phần tử cuối cùng là tên package.
- Go map package theo thư mục, nằm ở dưới $GOPATH.
- Ví dụ con vợ có đường dẫn là
github.com/user/hello
thì nó sẽ tìm ở$GOPATH/github.com/user/hello
- Ví dụ con vợ có đường dẫn là
- Các thư viện chuẩn thường chia các thư mục theo các giao thức và thuật tóan. VD:
crypto, container, encoding
.- Không có sự liên quan nào giữa các package cả. Chỉ là cách tổ chức file thôi.
- Nếu import trùng nhau thì có thể rename lại
import (
"runtime/pprof"
netpprof "net/http/pprof"
)
- Lưu ý là naming lại cũng nên theo quy tắc ban đầu: lowercase, KHÔNG under_score, KHÔNG mixedCaps.
Bad package name
-
Tránh các package name như dbrr dạng:
util, common, misc
, người dùng sẽ đ’ hiểu là bạn cung cấp gì ở đây cả. Càng ngày càng nhiều người đắp vào đây, và bùm… có ngay 1 đống hổ lốn vkl. -
Nếu gặp thì xử lý ntn?
-
VD:
package util
func NewStringSet(...string) map[string]bool {...}
func SortStringSet(map[string]bool) []string {...}
- Package trông có vẻ liên quan đến StringSet, nên đổi cmn package name về
stringset
có phải dễ hiểu hơn không?
package stringset
func New(...string) map[string]bool {...}
func Sort(map[string]bool) []string {...}
- Trông cũng tạm, mà chưa Gopher lắm. Tư duy ra thì nên sửa thế này:
- Tạo 1 type StringSet
- Hàm New trả về type mới
- Hàm Sort đưa vào làm method cho type
package stringset
type Set map[string]bool
func New(...string) Set {...}
func (s Set) Sort() []string {...}
- Package name sẽ nói lên con người của bạn, sẽ mang lại vận mệnh, may mắn, tài lộc cho maintainer :v. Vì vậy, hãy đặt package name sáng vào.
- Đừng dùng package cho tòan bộ các APIs của bạn. VD đừng đặt package kiểu:
api
cho tòan bộ apitypes
để lưu tòan bộ các typeinteraces
để lưư tòan bộ các interface.- ~> trông chán vl. Không Gopher tí nào.
- Tránh việc trùng với các package mặc định của Go. (VD:
io
,http
)
Anh có thể ví dụ 1 project về phong cách tổ chức các package và càng layer xử lý hợp lý được không ạ.
Em thấy có 1 số phong cách về cách tổ chức package này và vẫn mỗi cách có 1 số điều bất hợp lý riêng
ThíchĐã thích bởi 1 người
Anh thấy trong bài nêu khá rõ: tổ chức đơn giản thôi. Đừng gom chung utils vào.
Em có thể nói rõ vấn đề của mình ko? Nếu clear hơn thì a có thể giúp được đó.
ThíchThích
E gặp vấn đề ở tầng service.
Mỗi service xử lý 1 tập feature liên quan e sẽ để chung ở 1 package. Nhưng ví dụ service 1, cần sử dụng 1 hàm của service 2. Thì sẽ phải tách 1 hàm của service 2 thành 1 package nữa để 2 service dùng chung thì liệu như thế có hợp lý không hay có cách tổ chức khác hợp lý hơn ạ
ThíchĐã thích bởi 1 người
Anh thấy hợp lí. Tách nhỏ ra.
Và nên tổ chức kiểu “Gopher” như trong bài có nói:
– Tạo 1 type
– Viết hàm New()
– Đưa hàm util trở thành 1 method của type
“`
package stringset
type Set map[string]bool
func New(…string) Set {…}
func (s Set) Sort() []string {…}
“`
ThíchThích
Vâng, e cảm ơn….
ThíchThích