mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-08 04:57:36 +00:00
shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables: ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj (ghg-clone not named ghg: unrelated cargo binary owns that name) - cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files - fish keeps only shell-state functions; converted ones now hit PATH - cp-appid ignore gating moved to .local/bin path - dot_agents/skills: 82 skills materialized from dotstate storage so dot_claude skill symlinks resolve on fresh bootstrap; dotstate profile/symlinks.json released .agents/skills .bashrc .zshrc
This commit is contained in:
1 parent
6776366ce6
commit
6406208b35
637 files changed
+100297
-497
No files matched your search
@@ -0,0 +1,432 @@
|
||||
# Interface Design and Composition
|
||||
|
||||
## Small, Focused Interfaces
|
||||
|
||||
```go
|
||||
// Single-method interfaces (idiomatic Go)
|
||||
type Reader interface {
|
||||
Read(p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
type Writer interface {
|
||||
Write(p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
type Closer interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// Interface composition
|
||||
type ReadCloser interface {
|
||||
Reader
|
||||
Closer
|
||||
}
|
||||
|
||||
type WriteCloser interface {
|
||||
Writer
|
||||
Closer
|
||||
}
|
||||
|
||||
type ReadWriteCloser interface {
|
||||
Reader
|
||||
Writer
|
||||
Closer
|
||||
}
|
||||
```
|
||||
|
||||
## Accept Interfaces, Return Structs
|
||||
|
||||
```go
|
||||
package storage
|
||||
|
||||
import "io"
|
||||
|
||||
// Storage is the concrete type (struct)
|
||||
type Storage struct {
|
||||
baseDir string
|
||||
}
|
||||
|
||||
// NewStorage returns a concrete type
|
||||
func NewStorage(baseDir string) *Storage {
|
||||
return &Storage{baseDir: baseDir}
|
||||
}
|
||||
|
||||
// SaveFile accepts an interface for flexibility
|
||||
func (s *Storage) SaveFile(filename string, data io.Reader) error {
|
||||
// Implementation can work with any Reader
|
||||
// (file, network, buffer, etc.)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Usage allows dependency injection
|
||||
type Uploader interface {
|
||||
SaveFile(filename string, data io.Reader) error
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
uploader Uploader // Accept interface
|
||||
}
|
||||
|
||||
// NewService accepts interface for testing flexibility
|
||||
func NewService(uploader Uploader) *Service {
|
||||
return &Service{uploader: uploader}
|
||||
}
|
||||
```
|
||||
|
||||
## io.Reader and io.Writer Patterns
|
||||
|
||||
```go
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Chain readers with io.MultiReader
|
||||
func combineReaders() io.Reader {
|
||||
r1 := strings.NewReader("Hello ")
|
||||
r2 := strings.NewReader("World")
|
||||
return io.MultiReader(r1, r2)
|
||||
}
|
||||
|
||||
// Tee reader for duplicating reads
|
||||
func duplicateRead(r io.Reader, w io.Writer) io.Reader {
|
||||
return io.TeeReader(r, w) // Writes to w while reading from r
|
||||
}
|
||||
|
||||
// Limit reader to prevent reading too much
|
||||
func limitedRead(r io.Reader, n int64) io.Reader {
|
||||
return io.LimitReader(r, n)
|
||||
}
|
||||
|
||||
// Custom Reader implementation
|
||||
type UppercaseReader struct {
|
||||
src io.Reader
|
||||
}
|
||||
|
||||
func (u *UppercaseReader) Read(p []byte) (n int, err error) {
|
||||
n, err = u.src.Read(p)
|
||||
for i := 0; i < n; i++ {
|
||||
if p[i] >= 'a' && p[i] <= 'z' {
|
||||
p[i] = p[i] - 32
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Custom Writer implementation
|
||||
type CountingWriter struct {
|
||||
w io.Writer
|
||||
count int64
|
||||
}
|
||||
|
||||
func (cw *CountingWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = cw.w.Write(p)
|
||||
cw.count += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (cw *CountingWriter) BytesWritten() int64 {
|
||||
return cw.count
|
||||
}
|
||||
```
|
||||
|
||||
## Embedding for Composition
|
||||
|
||||
```go
|
||||
import "sync"
|
||||
|
||||
// Embed to extend behavior
|
||||
type SafeCounter struct {
|
||||
mu sync.Mutex
|
||||
m map[string]int
|
||||
}
|
||||
|
||||
func (sc *SafeCounter) Inc(key string) {
|
||||
sc.mu.Lock()
|
||||
defer sc.mu.Unlock()
|
||||
sc.m[key]++
|
||||
}
|
||||
|
||||
// Embed interface to add default behavior
|
||||
type Logger interface {
|
||||
Log(msg string)
|
||||
}
|
||||
|
||||
type NoOpLogger struct{}
|
||||
|
||||
func (NoOpLogger) Log(msg string) {}
|
||||
|
||||
type Service struct {
|
||||
Logger // Embedded interface (default implementation can be provided)
|
||||
}
|
||||
|
||||
func NewService(logger Logger) *Service {
|
||||
if logger == nil {
|
||||
logger = NoOpLogger{} // Provide default
|
||||
}
|
||||
return &Service{Logger: logger}
|
||||
}
|
||||
|
||||
// Now Service.Log() is available
|
||||
```
|
||||
|
||||
## Interface Satisfaction Verification
|
||||
|
||||
```go
|
||||
import "io"
|
||||
|
||||
// Compile-time interface verification
|
||||
var _ io.Reader = (*MyReader)(nil)
|
||||
var _ io.Writer = (*MyWriter)(nil)
|
||||
var _ io.Closer = (*MyCloser)(nil)
|
||||
|
||||
type MyReader struct{}
|
||||
|
||||
func (m *MyReader) Read(p []byte) (n int, err error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
type MyWriter struct{}
|
||||
|
||||
func (m *MyWriter) Write(p []byte) (n int, err error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type MyCloser struct{}
|
||||
|
||||
func (m *MyCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Functional Options Pattern
|
||||
|
||||
```go
|
||||
package server
|
||||
|
||||
import "time"
|
||||
|
||||
type Server struct {
|
||||
host string
|
||||
port int
|
||||
timeout time.Duration
|
||||
maxConns int
|
||||
enableLogger bool
|
||||
}
|
||||
|
||||
// Option is a functional option for configuring Server
|
||||
type Option func(*Server)
|
||||
|
||||
func WithHost(host string) Option {
|
||||
return func(s *Server) {
|
||||
s.host = host
|
||||
}
|
||||
}
|
||||
|
||||
func WithPort(port int) Option {
|
||||
return func(s *Server) {
|
||||
s.port = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(s *Server) {
|
||||
s.timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func WithMaxConnections(max int) Option {
|
||||
return func(s *Server) {
|
||||
s.maxConns = max
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogger(enabled bool) Option {
|
||||
return func(s *Server) {
|
||||
s.enableLogger = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// NewServer creates a server with functional options
|
||||
func NewServer(opts ...Option) *Server {
|
||||
// Defaults
|
||||
s := &Server{
|
||||
host: "localhost",
|
||||
port: 8080,
|
||||
timeout: 30 * time.Second,
|
||||
maxConns: 100,
|
||||
}
|
||||
|
||||
// Apply options
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Usage:
|
||||
// server := NewServer(
|
||||
// WithHost("0.0.0.0"),
|
||||
// WithPort(9000),
|
||||
// WithTimeout(60 * time.Second),
|
||||
// WithLogger(true),
|
||||
// )
|
||||
```
|
||||
|
||||
## Interface Segregation
|
||||
|
||||
```go
|
||||
// Bad: Fat interface
|
||||
type BadRepository interface {
|
||||
Create(item Item) error
|
||||
Read(id string) (Item, error)
|
||||
Update(item Item) error
|
||||
Delete(id string) error
|
||||
List() ([]Item, error)
|
||||
Search(query string) ([]Item, error)
|
||||
Count() (int, error)
|
||||
}
|
||||
|
||||
// Good: Segregated interfaces
|
||||
type Creator interface {
|
||||
Create(item Item) error
|
||||
}
|
||||
|
||||
type Reader interface {
|
||||
Read(id string) (Item, error)
|
||||
}
|
||||
|
||||
type Updater interface {
|
||||
Update(item Item) error
|
||||
}
|
||||
|
||||
type Deleter interface {
|
||||
Delete(id string) error
|
||||
}
|
||||
|
||||
type Lister interface {
|
||||
List() ([]Item, error)
|
||||
}
|
||||
|
||||
// Compose only what you need
|
||||
type ReadWriter interface {
|
||||
Reader
|
||||
Creator
|
||||
}
|
||||
|
||||
type FullRepository interface {
|
||||
Creator
|
||||
Reader
|
||||
Updater
|
||||
Deleter
|
||||
Lister
|
||||
}
|
||||
```
|
||||
|
||||
## Type Assertions and Type Switches
|
||||
|
||||
```go
|
||||
import "fmt"
|
||||
|
||||
// Safe type assertion
|
||||
func processValue(v interface{}) {
|
||||
// Two-value assertion (safe)
|
||||
if str, ok := v.(string); ok {
|
||||
fmt.Println("String:", str)
|
||||
return
|
||||
}
|
||||
|
||||
// Type switch
|
||||
switch val := v.(type) {
|
||||
case int:
|
||||
fmt.Println("Int:", val)
|
||||
case string:
|
||||
fmt.Println("String:", val)
|
||||
case bool:
|
||||
fmt.Println("Bool:", val)
|
||||
default:
|
||||
fmt.Println("Unknown type")
|
||||
}
|
||||
}
|
||||
|
||||
// Check for optional interface methods
|
||||
type Flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
func writeAndFlush(w io.Writer, data []byte) error {
|
||||
if _, err := w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if Writer also implements Flusher
|
||||
if flusher, ok := w.(Flusher); ok {
|
||||
return flusher.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Injection via Interfaces
|
||||
|
||||
```go
|
||||
package app
|
||||
|
||||
import "context"
|
||||
|
||||
// Define interfaces for dependencies
|
||||
type UserRepository interface {
|
||||
GetUser(ctx context.Context, id string) (*User, error)
|
||||
SaveUser(ctx context.Context, user *User) error
|
||||
}
|
||||
|
||||
type EmailSender interface {
|
||||
SendEmail(ctx context.Context, to, subject, body string) error
|
||||
}
|
||||
|
||||
// Service depends on interfaces
|
||||
type UserService struct {
|
||||
repo UserRepository
|
||||
mailer EmailSender
|
||||
}
|
||||
|
||||
func NewUserService(repo UserRepository, mailer EmailSender) *UserService {
|
||||
return &UserService{
|
||||
repo: repo,
|
||||
mailer: mailer,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UserService) RegisterUser(ctx context.Context, email string) error {
|
||||
user := &User{Email: email}
|
||||
if err := s.repo.SaveUser(ctx, user); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.mailer.SendEmail(ctx, email, "Welcome", "Thanks for registering!")
|
||||
}
|
||||
|
||||
// Easy to mock in tests
|
||||
type MockUserRepository struct{}
|
||||
|
||||
func (m *MockUserRepository) GetUser(ctx context.Context, id string) (*User, error) {
|
||||
return &User{ID: id}, nil
|
||||
}
|
||||
|
||||
func (m *MockUserRepository) SaveUser(ctx context.Context, user *User) error {
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Pattern | Use Case | Key Principle |
|
||||
|---------|----------|---------------|
|
||||
| Small interfaces | Flexibility | Single-method interfaces |
|
||||
| Accept interfaces | Testability | Depend on abstractions |
|
||||
| Return structs | Clarity | Concrete return types |
|
||||
| io.Reader/Writer | I/O operations | Standard library integration |
|
||||
| Embedding | Composition | Extend behavior without inheritance |
|
||||
| Functional options | Configuration | Flexible constructors |
|
||||
| Type assertions | Runtime checks | Safe downcasting |
|
||||
Reference in new issue
Block a user