Claude-skill-registry go-interfaces
Use when Go interfaces including interface design, duck typing, and composition patterns. Use when designing Go APIs and abstractions.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/go-interfaces" ~/.claude/skills/majiayu000-claude-skill-registry-go-interfaces && rm -rf "$T"
manifest:
skills/data/go-interfaces/SKILL.mdsource content
Go Interfaces
Master Go's interface system for creating flexible, decoupled code through implicit implementation and composition patterns.
Basic Interfaces
Defining and implementing interfaces:
package main import "fmt" // Define interface type Writer interface { Write(p []byte) (n int, err error) } // Implement interface (implicit) type ConsoleWriter struct{} func (cw ConsoleWriter) Write(p []byte) (n int, err error) { fmt.Print(string(p)) return len(p), nil } func main() { var w Writer = ConsoleWriter{} w.Write([]byte("Hello, World!\n")) }
Multiple methods in interface:
type Reader interface { Read(p []byte) (n int, err error) } type ReadWriter interface { Read(p []byte) (n int, err error) Write(p []byte) (n int, err error) } // Implement ReadWriter type File struct { name string } func (f *File) Read(p []byte) (n int, err error) { // Implementation return 0, nil } func (f *File) Write(p []byte) (n int, err error) { // Implementation return len(p), nil }
Empty Interface
Using interface{} (any in Go 1.18+):
// Accepts any type func printValue(v interface{}) { fmt.Println(v) } // Modern syntax (Go 1.18+) func printAny(v any) { fmt.Println(v) } func main() { printValue(42) printValue("hello") printValue(true) printAny(3.14) }
Type assertions:
func processValue(v interface{}) { // Type assertion if str, ok := v.(string); ok { fmt.Println("String:", str) } // Type switch switch val := v.(type) { case int: fmt.Println("Integer:", val) case string: fmt.Println("String:", val) case bool: fmt.Println("Boolean:", val) default: fmt.Println("Unknown type") } }
Interface Composition
Embedding interfaces:
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 } // Compose interfaces type ReadWriter interface { Reader Writer } type ReadWriteCloser interface { Reader Writer Closer } // Standard library example import "io" func useReadWriteCloser(rwc io.ReadWriteCloser) { // Can call Read, Write, and Close rwc.Write([]byte("data")) rwc.Close() }
Common Interfaces
Standard library interfaces:
// Stringer interface type Stringer interface { String() string } type Person struct { Name string Age int } func (p Person) String() string { return fmt.Sprintf("%s (%d years old)", p.Name, p.Age) } // error interface type error interface { Error() string } type MyError struct { Message string } func (e MyError) Error() string { return e.Message } // sort.Interface type Interface interface { Len() int Less(i, j int) bool Swap(i, j int) } type ByAge []Person func (a ByAge) Len() int { return len(a) } func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
Interface Design Patterns
Small interfaces:
// Good: small, focused interfaces type Getter interface { Get(key string) (value string, exists bool) } type Setter interface { Set(key, value string) } type Deleter interface { Delete(key string) } // Compose as needed type Cache interface { Getter Setter Deleter }
Accept interfaces, return structs:
// Accept interface parameter func processReader(r io.Reader) error { data, err := io.ReadAll(r) if err != nil { return err } fmt.Println(string(data)) return nil } // Return concrete type func newConfig() *Config { return &Config{ Host: "localhost", Port: 8080, } } type Config struct { Host string Port int }
Nil Interfaces
Understanding nil interfaces:
func checkNil() { var i interface{} fmt.Println(i == nil) // true var p *Person i = p fmt.Println(i == nil) // false! (type is set, value is nil) // Proper nil check v, ok := i.(*Person) fmt.Println(v == nil, ok) // true, true }
Interface Satisfaction
Checking interface implementation:
// Compile-time check var _ io.Writer = (*MyWriter)(nil) var _ io.Reader = (*MyReader)(nil) type MyWriter struct{} func (w *MyWriter) Write(p []byte) (n int, err error) { return len(p), nil } // If MyWriter doesn't implement Writer, compilation fails
Duck Typing
Implicit interface satisfaction:
// No explicit "implements" keyword needed type Duck interface { Quack() Walk() } type RealDuck struct{} func (d RealDuck) Quack() { fmt.Println("Quack!") } func (d RealDuck) Walk() { fmt.Println("Waddle waddle") } type Robot struct{} func (r Robot) Quack() { fmt.Println("Beep boop quack") } func (r Robot) Walk() { fmt.Println("*mechanical walking sounds*") } func makeDuckDoThings(d Duck) { d.Quack() d.Walk() } func main() { makeDuckDoThings(RealDuck{}) makeDuckDoThings(Robot{}) }
Polymorphism
Using interfaces for polymorphism:
type Shape interface { Area() float64 Perimeter() float64 } type Rectangle struct { Width, Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) } type Circle struct { Radius float64 } func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius } func (c Circle) Perimeter() float64 { return 2 * 3.14159 * c.Radius } func printShapeInfo(s Shape) { fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter()) } func main() { shapes := []Shape{ Rectangle{Width: 10, Height: 5}, Circle{Radius: 7}, } for _, shape := range shapes { printShapeInfo(shape) } }
Dependency Injection
Using interfaces for testability:
// Define interface for dependency type UserRepository interface { GetUser(id int) (*User, error) SaveUser(user *User) error } // Production implementation type PostgresUserRepo struct { db *sql.DB } func (r *PostgresUserRepo) GetUser(id int) (*User, error) { // Database query return &User{}, nil } func (r *PostgresUserRepo) SaveUser(user *User) error { // Database insert/update return nil } // Test implementation type MockUserRepo struct { users map[int]*User } func (m *MockUserRepo) GetUser(id int) (*User, error) { user, exists := m.users[id] if !exists { return nil, errors.New("user not found") } return user, nil } func (m *MockUserRepo) SaveUser(user *User) error { m.users[user.ID] = user return nil } // Service depends on interface, not concrete type type UserService struct { repo UserRepository } func (s *UserService) GetUserName(id int) (string, error) { user, err := s.repo.GetUser(id) if err != nil { return "", err } return user.Name, nil } type User struct { ID int Name string }
Builder Pattern with Interfaces
Fluent interface pattern:
type QueryBuilder interface { Select(fields ...string) QueryBuilder From(table string) QueryBuilder Where(condition string) QueryBuilder Build() string } type sqlQueryBuilder struct { selectFields []string fromTable string whereClause string } func NewQueryBuilder() QueryBuilder { return &sqlQueryBuilder{} } func (b *sqlQueryBuilder) Select(fields ...string) QueryBuilder { b.selectFields = fields return b } func (b *sqlQueryBuilder) From(table string) QueryBuilder { b.fromTable = table return b } func (b *sqlQueryBuilder) Where(condition string) QueryBuilder { b.whereClause = condition return b } func (b *sqlQueryBuilder) Build() string { query := "SELECT " + strings.Join(b.selectFields, ", ") query += " FROM " + b.fromTable if b.whereClause != "" { query += " WHERE " + b.whereClause } return query } func main() { query := NewQueryBuilder(). Select("id", "name", "email"). From("users"). Where("age > 18"). Build() fmt.Println(query) }
Strategy Pattern
Implementing strategy pattern:
type PaymentStrategy interface { Pay(amount float64) error } type CreditCardPayment struct { CardNumber string } func (c *CreditCardPayment) Pay(amount float64) error { fmt.Printf("Paying %.2f with credit card %s\n", amount, c.CardNumber) return nil } type PayPalPayment struct { Email string } func (p *PayPalPayment) Pay(amount float64) error { fmt.Printf("Paying %.2f via PayPal to %s\n", amount, p.Email) return nil } type ShoppingCart struct { paymentMethod PaymentStrategy } func (cart *ShoppingCart) SetPaymentMethod(pm PaymentStrategy) { cart.paymentMethod = pm } func (cart *ShoppingCart) Checkout(amount float64) error { return cart.paymentMethod.Pay(amount) } func main() { cart := &ShoppingCart{} cart.SetPaymentMethod(&CreditCardPayment{CardNumber: "1234-5678"}) cart.Checkout(100.00) cart.SetPaymentMethod(&PayPalPayment{Email: "user@example.com"}) cart.Checkout(50.00) }
Adapter Pattern
Adapting interfaces:
// Third-party logger type ThirdPartyLogger struct{} func (t *ThirdPartyLogger) LogMessage(msg string, level int) { fmt.Printf("[Level %d] %s\n", level, msg) } // Our application interface type Logger interface { Info(msg string) Error(msg string) } // Adapter type LoggerAdapter struct { thirdParty *ThirdPartyLogger } func (a *LoggerAdapter) Info(msg string) { a.thirdParty.LogMessage(msg, 0) } func (a *LoggerAdapter) Error(msg string) { a.thirdParty.LogMessage(msg, 2) } func useLogger(logger Logger) { logger.Info("Application started") logger.Error("An error occurred") } func main() { adapter := &LoggerAdapter{ thirdParty: &ThirdPartyLogger{}, } useLogger(adapter) }
When to Use This Skill
Use go-interfaces when you need to:
- Define contracts for behavior without implementation
- Enable polymorphism and code reuse
- Create testable code with dependency injection
- Implement design patterns (strategy, adapter, etc.)
- Build plugin systems or extensible architectures
- Decouple components in large applications
- Mock dependencies in tests
- Follow SOLID principles in Go
- Create flexible, maintainable APIs
- Support multiple implementations of same behavior
Best Practices
- Keep interfaces small and focused (1-3 methods)
- Accept interfaces, return concrete types
- Define interfaces where they're used, not implemented
- Use interface composition for complex interfaces
- Don't use empty interface unless absolutely necessary
- Verify interface implementation at compile time
- Document expected behavior in interface comments
- Prefer many small interfaces over large ones
- Use standard library interfaces when applicable
- Name interfaces with -er suffix (Reader, Writer, etc.)
Common Pitfalls
- Making interfaces too large or generic
- Defining unused interfaces "just in case"
- Returning interfaces instead of concrete types
- Not checking for nil interface values properly
- Over-abstracting simple code
- Forgetting that interfaces are satisfied implicitly
- Using empty interface excessively
- Not documenting interface contracts
- Creating interfaces for single implementation
- Confusing nil value vs nil interface