算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
go-design-patterns【设计与创意】Idiomatic Go design patterns: functional options, builder, factory, strategy, middleware chain, pub/sub, and other patterns adapted for Go's type system. Use when: "design pattern", "functional options", "builder pattern", "factory pattern", "strategy pattern", "middleware chain", "option pattern", "how to structure this". Not for: interface design (…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Go favors composition over inheritance and simplicity over abstraction. These patterns are idiomatic Go — not Java patterns ported to Go.
Detailed reference material, loaded on demand:
references/creation-patterns.md — functional options (full example),
options vs config struct, constructors, factory.references/behavioral-patterns.md — strategy, middleware/decorator,
result type, defer cleanup, sentinel vs zero values.Read a reference file only when the summary below is not enough.
| Need | Pattern | Reference |
|---|---|---|
| Constructor with many optional settings | Functional options | creation-patterns.md |
| Config loaded from file/env, mostly required fields | Config struct | creation-patterns.md |
| Enforce invariants at creation | Constructor returning error | creation-patterns.md |
| Pick implementation from runtime config | Factory returning interface | creation-patterns.md |
| Swap simple behavior at runtime | Strategy via function type | behavioral-patterns.md |
| Swap complex behavior at runtime | Strategy via interface | behavioral-patterns.md |
| Wrap cross-cutting concerns (log, cache, metrics) | Middleware / decorator | behavioral-patterns.md |
| Value-or-error in concurrent pipelines | Result[T] struct | behavioral-patterns.md |
type Option func(*Server)
func WithAddr(addr string) Option {
return func(s *Server) { s.addr = addr }
}
func NewServer(opts ...Option) *Server {
s := &Server{
addr: ":8080", // sensible defaults first
readTimeout: 5 * time.Second,
logger: slog.Default(),
}
for _, opt := range opts {
opt(s)
}
return s
}
srv := NewServer(WithAddr(":9090"))
Use when: many optional parameters with sensible defaults, API evolves over time (new options don't break callers), options need validation. Use a plain config struct instead when most fields are required or the configuration is deserialized from file/env.
// ✅ Good — constructor enforces invariants
func NewUserService(repo UserRepository, logger *slog.Logger) (*UserService, error) {
if repo == nil {
return nil, errors.New("user service: nil repository")
}
return &UserService{repo: repo, logger: logger}, nil
}
// ❌ Bad — struct literal with no validation
svc := &UserService{} // nil dependencies → panic at runtime
Return the interface, not a concrete type. The factory is the only place that knows about concrete implementations:
func NewStore(cfg Config) (Store, error) {
switch cfg.StoreType {
case "redis":
return newRedisStore(cfg.RedisAddr)
case "memory":
return newMemoryStore(), nil
default:
return nil, fmt.Errorf("unknown store type: %s", cfg.StoreType)
}
}
The standard HTTP composition pattern:
type Middleware func(http.Handler) http.Handler
func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
handler := Chain(appHandler, Recoverer, RequestID, Logger, Auth)
The same shape works for any interface: stack decorators as
cache → logging → metrics → actual repo
(see references/behavioral-patterns.md).
Prefer types whose zero value is useful (sync.Mutex, bytes.Buffer,
nil slices). Reach for sentinel wrappers or pointers only when the zero
value is ambiguous as an input (nil *float64 = "not configured").
// ❌ God interface — too many methods
type Service interface {
GetUser(ctx context.Context, id string) (*User, error)
CreateUser(ctx context.Context, u *User) error
DeleteUser(ctx context.Context, id string) error
ListOrders(ctx context.Context, userID string) ([]Order, error)
// 20 more methods...
}
// → Split into focused interfaces: UserReader, UserWriter, OrderLister
// ❌ Premature abstraction — interface for one implementation
type UserCache interface {
Get(key string) (*User, bool)
Set(key string, user *User)
}
// If there's only ever one implementation, use the concrete type.
// Extract an interface when a second consumer or implementation appears.
// ❌ Java-style inheritance simulation
type BaseService struct{ /* ... */ }
type UserService struct{ BaseService } // embedding is NOT inheritance
// → Use composition: UserService has a dependency, not a parent.
func(http.Handler) http.Handler signaturedefer used for all resource cleanup (files, connections, locks)