Go telah menjadi kuasa besar dalam pembangunan bahagian belakang moden, perkhidmatan awan dan alat DevOps. Mari terokai cara menulis kod Go idiomatik yang memanfaatkan kekuatan bahasa.
Pertama, mari kita sediakan struktur projek Go moden:
# Initialize a new module go mod init myproject # Project structure myproject/ ├── cmd/ │ └── api/ │ └── main.go ├── internal/ │ ├── handlers/ │ ├── models/ │ └── services/ ├── pkg/ │ └── utils/ ├── go.mod └── go.sum
Berikut ialah contoh program Go yang tersusun dengan baik:
package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" ) // Server configuration type Config struct { Port string ReadTimeout time.Duration WriteTimeout time.Duration ShutdownTimeout time.Duration } // Application represents our web server type Application struct { config Config logger *log.Logger router *http.ServeMux } // NewApplication creates a new application instance func NewApplication(cfg Config) *Application { logger := log.New(os.Stdout, "[API] ", log.LstdFlags) return &Application{ config: cfg, logger: logger, router: http.NewServeMux(), } } // setupRoutes configures all application routes func (app *Application) setupRoutes() { app.router.HandleFunc("/health", app.healthCheckHandler) app.router.HandleFunc("/api/v1/users", app.handleUsers) } // Run starts the server and handles graceful shutdown func (app *Application) Run() error { // Setup routes app.setupRoutes() // Create server srv := &http.Server{ Addr: ":" + app.config.Port, Handler: app.router, ReadTimeout: app.config.ReadTimeout, WriteTimeout: app.config.WriteTimeout, } // Channel to listen for errors coming from the listener. serverErrors := make(chan error, 1) // Start the server go func() { app.logger.Printf("Starting server on port %s", app.config.Port) serverErrors <- srv.ListenAndServe() }() // Listen for OS signals shutdown := make(chan os.Signal, 1) signal.Notify(shutdown, os.Interrupt, syscall.SIGTERM) // Block until we receive a signal or an error select { case err := <-serverErrors: return fmt.Errorf("server error: %w", err) case <-shutdown: app.logger.Println("Starting shutdown...") // Create context for shutdown ctx, cancel := context.WithTimeout( context.Background(), app.config.ShutdownTimeout, ) defer cancel() // Gracefully shutdown the server err := srv.Shutdown(ctx) if err != nil { return fmt.Errorf("graceful shutdown failed: %w", err) } } return nil }
Sistem antara muka Go dan pengendalian ralat ialah ciri utama:
// UserService defines the interface for user operations type UserService interface { GetUser(ctx context.Context, id string) (*User, error) CreateUser(ctx context.Context, user *User) error UpdateUser(ctx context.Context, user *User) error DeleteUser(ctx context.Context, id string) error } // Custom error types type NotFoundError struct { Resource string ID string } func (e *NotFoundError) Error() string { return fmt.Sprintf("%s with ID %s not found", e.Resource, e.ID) } // Implementation type userService struct { db *sql.DB logger *log.Logger } func (s *userService) GetUser(ctx context.Context, id string) (*User, error) { user := &User{} err := s.db.QueryRowContext( ctx, "SELECT id, name, email FROM users WHERE id = ", id, ).Scan(&user.ID, &user.Name, &user.Email) if err == sql.ErrNoRows { return nil, &NotFoundError{Resource: "user", ID: id} } if err != nil { return nil, fmt.Errorf("querying user: %w", err) } return user, nil }
Gorutin dan saluran Go menjadikan pengaturcaraan serentak menjadi mudah:
// Worker pool pattern func processItems(items []string, numWorkers int) error { jobs := make(chan string, len(items)) results := make(chan error, len(items)) // Start workers for w := 0; w < numWorkers; w++ { go worker(w, jobs, results) } // Send jobs to workers for _, item := range items { jobs <- item } close(jobs) // Collect results for range items { if err := <-results; err != nil { return err } } return nil } func worker(id int, jobs <-chan string, results chan<- error) { for item := range jobs { results <- processItem(item) } } // Rate limiting func rateLimiter[T any](input <-chan T, limit time.Duration) <-chan T { output := make(chan T) ticker := time.NewTicker(limit) go func() { defer close(output) defer ticker.Stop() for item := range input { <-ticker.C output <- item } }() return output }
Go mempunyai sokongan ujian terbina dalam yang sangat baik:
// user_service_test.go package service import ( "context" "testing" "time" ) func TestUserService(t *testing.T) { // Table-driven tests tests := []struct { name string userID string want *User wantErr bool }{ { name: "valid user", userID: "123", want: &User{ ID: "123", Name: "Test User", }, wantErr: false, }, { name: "invalid user", userID: "999", want: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { svc := NewUserService(testDB) got, err := svc.GetUser(context.Background(), tt.userID) if (err != nil) != tt.wantErr { t.Errorf("GetUser() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("GetUser() = %v, want %v", got, tt.want) } }) } } // Benchmarking example func BenchmarkUserService_GetUser(b *testing.B) { svc := NewUserService(testDB) ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = svc.GetUser(ctx, "123") } }
Go memudahkan untuk membuat profil dan mengoptimumkan kod:
// Use sync.Pool for frequently allocated objects var bufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) }, } func processRequest(data []byte) string { buf := bufferPool.Get().(*bytes.Buffer) defer bufferPool.Put(buf) buf.Reset() buf.Write(data) // Process data... return buf.String() } // Efficiently handle JSON type User struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` CreatedAt time.Time `json:"created_at"` } func (u *User) MarshalJSON() ([]byte, error) { type Alias User return json.Marshal(&struct { *Alias CreatedAt string `json:"created_at"` }{ Alias: (*Alias)(u), CreatedAt: u.CreatedAt.Format(time.RFC3339), }) }
Kesederhanaan dan ciri berkuasa Go menjadikannya pilihan yang sangat baik untuk pembangunan moden. Ambilan penting:
Apakah aspek pembangunan Go yang paling anda minati? Kongsi pengalaman anda dalam komen di bawah!
Atas ialah kandungan terperinci Menguasai Go: Panduan Praktikal Pembangunan Golang Moden. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!