Polymorphism lets code work through a common abstraction while different implementations provide type-specific behavior. In Go, it does not come from a class hierarchy: Go has a flat type structure and does not support classical inheritance. Runtime polymorphism is provided by interfaces. section 1.5.1
Struct embedding is composition, not inheritance. An embedding struct gains convenient access to embedded fields and methods, but the two struct types remain distinct and cannot be substituted for one another merely because one embeds the other. Treating embedding as inheritance is a common mistake; use interfaces when interchangeable runtime behavior is required. section 1.5.1
Figure 1.6 Inheritance creates a type hierarchy; embedding does not. In inheritance, you can pass parent (square) and child (circle) types interchangeably to a function that expects only the parent type. With embedding, the function can work only with the specified type. Embedding does not form a type hierarchy; it simply makes one type’s methods and data part of another type.
Contrast a type hierarchy, where parent and child values are interchangeable, with embedding, which reuses members without creating that relationship. section 1.5.1
Polymorphism can also sit behind a concrete API. A concrete type can expose stable, common operations while delegating technology-specific work to internal interfaces. This keeps callers independent of implementation details without forcing every implementation to satisfy one large exported interface. section 10.1 section 10.6
The database driver pattern illustrates this design:
- Call sql.Open with a driver name and a driver-specific connection string to obtain a *sql.DB. section 10.1
- Use *sql.DB for shared work such as queries, connection pooling, and automatic retries rather than managing individual connections. section 10.1
- Let database drivers communicate with SQLite, PostgreSQL, or other database technologies through sql/driver interfaces. section 10.1
- Allow optional driver capabilities to be detected internally with type assertions, so the concrete API can gain features without breaking existing implementations. section 10.1 section 10.6
One API, multiple databases
The same sql.Open call shape can select SQLite with "sqlite" and "file:links.db", or PostgreSQL with "postgres" and "dbname=links sslmode=disable". Application code works with *sql.DB rather than database-specific connection behavior; the selected driver handles the underlying technology. section 10.1
Keep interfaces focused on observed shared behavior. Large interfaces make implementations harder to write and can break existing code when methods are added. Consumer-defined interfaces help express actual requirements, while a concrete type can evolve behind a uniform API. section 10.1 section 10.6








