golang-structs-interfaces
Goの構造体とインターフェース設計パターンに関するスキルです。コンポジションや埋め込み、型アサーション・型スイッチ、インターフェース分離、インターフェースを用いた依存性注入、JSON/YAML/DBシリアライズ向けの構造体フィールドタグ、ポインタレシーバと値レシーバの使い分けを扱います。Goの型設計・インターフェース定義・実装、「インターフェースを受け取り構造体を返す」原則やコンパイル時インターフェースチェック、小さなインターフェースの合成など、Go型システムに関わる場面で活用してください。
description の原文を見る
Golang struct and interface design patterns — composition, embedding, type assertions, type switches, interface segregation, dependency injection via interfaces, struct field tags, and pointer vs value receivers. Use this skill when designing Go types, defining or implementing interfaces, embedding structs or interfaces, writing type assertions or type switches, adding struct field tags for JSON/YAML/DB serialization, or choosing between pointer and value receivers. Also use when the user asks about "accept interfaces, return structs", compile-time interface checks, or composing small interfaces into larger ones.
SKILL.md 本文
Persona: あなたは Go 型システムの設計者です。小さく構成可能なインターフェースとコンクリート返却型を好みます。テスト可能性と明確性を設計し、抽象化のためだけではありません。
コミュニティデフォルト。
samber/cc-skills-golang@golang-structs-interfacesスキルを明示的に上書きする会社スキルが優先されます。
Go Structs & Interfaces
インターフェース設計の原則
インターフェースを小さく保つ
「インターフェースが大きいほど、抽象化は弱くなる。」— Go Proverbs
インターフェースは 1~3 個のメソッドを持つべきです。小さなインターフェースは実装、モック、構成が簡単です。より大きなコントラクトが必要な場合は、小さなインターフェースから構成します:
→ インターフェース命名規則 (メソッド + "-er" サフィックス、標準的な名前) については samber/cc-skills-golang@golang-naming スキルを参照してください。
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// 小さなインターフェースから構成
type ReadWriter interface {
Reader
Writer
}
小さなインターフェースから大きなインターフェースを構成します:
type ReadWriteCloser interface {
io.Reader
io.Writer
io.Closer
}
インターフェースは消費される場所で定義する
インターフェースは消費者に属します。
インターフェースは実装されている場所ではなく、消費される場所で定義する必要があります。これにより、消費者がコントラクトをコントロールでき、インターフェースのためだけにパッケージをインポートするのを避けられます。
// package notification — 必要なものだけを定義
type Sender interface {
Send(to, body string) error
}
type Service struct {
sender Sender
}
email パッケージは具体的な Client struct をエクスポートしますが、Sender について知る必要はありません。
インターフェースを受け入れ、struct を返す
関数はインターフェースパラメータを受け入れて柔軟性を得、コンクリート型を返して明確性を得る必要があります。呼び出し元は返された型のフィールドとメソッドへの完全なアクセスを得ます。上流の消費者は必要に応じて結果をインターフェース変数に割り当てることができます。
// 良い — インターフェースを受け入れ、コンクリート型を返す
func NewService(store UserStore) *Service { ... }
// 悪い — コンストラクタからインターフェースを返してはいけません
func NewService(store UserStore) ServiceInterface { ... }
インターフェースを性急に作成しない
「インターフェースで設計するのではなく、発見する。」
インターフェースを性急に作成してはいけません — 2 つ以上の実装またはテスト可能性の要件を待ちます。性急なインターフェースは値のない間接参照を追加します。具体型で始めます。2 番目の消費者またはテストモックが必要な場合、後でインターフェースを抽出します。
// 悪い — 単一の実装を持つ性急なインターフェース
type UserRepository interface {
FindByID(ctx context.Context, id string) (*User, error)
}
type userRepository struct { db *sql.DB }
// 良い — 具体型で始め、後で必要になったらインターフェースを抽出する
type UserRepository struct { db *sql.DB }
ゼロ値を有用にする
明示的な初期化なしで機能する struct を設計します。適切に設計されたゼロ値はコンストラクタボイラープレートを減らし、nil 関連のバグを防ぎます:
// 良い — ゼロ値はすぐに使用できます
var buf bytes.Buffer
buf.WriteString("hello")
var mu sync.Mutex
mu.Lock()
// 悪い — ゼロ値は壊れており、コンストラクタが必要です
type Registry struct {
items map[string]Item // nil マップ、書き込みでパニック
}
// 良い — 遅延初期化がゼロ値を保護
func (r *Registry) Register(name string, item Item) {
if r.items == nil {
r.items = make(map[string]Item)
}
r.items[name] = item
}
特定の型が適切な場合に any / interface{} を避ける
Go 1.18+ 以降、any よりもジェネリクスを型安全な操作に優先する必要があります。any は型が本当に不明な true な境界でのみ使用します (JSON デコード、reflection など):
// 悪い — 型安全性を失う
func Contains(slice []any, target any) bool { ... }
// 良い — ジェネリック、型安全
func Contains[T comparable](slice []T, target T) bool { ... }
主要な標準ライブラリインターフェース
| インターフェース | パッケージ | メソッド |
|---|---|---|
Reader | io | Read(p []byte) (n int, err error) |
Writer | io | Write(p []byte) (n int, err error) |
Closer | io | Close() error |
Stringer | fmt | String() string |
error | builtin | Error() string |
Handler | net/http | ServeHTTP(ResponseWriter, *Request) |
Marshaler | encoding/json | MarshalJSON() ([]byte, error) |
Unmarshaler | encoding/json | UnmarshalJSON([]byte) error |
標準的なメソッドシグネチャを守る必要があります。型に String() メソッドがある場合、fmt.Stringer と一致する必要があります。ToString() や ReadData() を発明しないでください。
コンパイル時インターフェースチェック
ブランク識別子割り当てを使用して、型がコンパイル時にインターフェースを実装していることを確認します。型定義の近くに配置します:
var _ io.ReadWriter = (*MyBuffer)(nil)
これはランタイムでコストはありません。MyBuffer が io.ReadWriter を満たさなくなった場合、ビルドは即座に失敗します。
型アサーション & 型スイッチ
安全な型アサーション
型アサーションはパニックを避けるためにカンマ ok フォームを使用する必要があります:
// 良い — 安全
s, ok := val.(string)
if !ok {
// ハンドル
}
// 悪い — val が文字列でない場合、パニック
s := val.(string)
型スイッチ
インターフェース値の動的型を発見します:
switch v := val.(type) {
case string:
fmt.Println(v)
case int:
fmt.Println(v * 2)
case io.Reader:
io.Copy(os.Stdout, v)
default:
fmt.Printf("unexpected type %T\n", v)
}
型アサーションで追加機能をオプショナルにする
値が追加機能をサポートしているかどうかを確認し、事前には必要としません:
type Flusher interface {
Flush() error
}
func writeData(w io.Writer, data []byte) error {
if _, err := w.Write(data); err != nil {
return err
}
// ライターがサポートしている場合のみフラッシュ
if f, ok := w.(Flusher); ok {
return f.Flush()
}
return nil
}
このパターンは標準ライブラリで広く使われています (http.Flusher, io.ReaderFrom など)。
Struct & インターフェース埋め込み
Struct 埋め込み
埋め込みは内部型のメソッドとフィールドを外部型に昇格させます — 継承ではなく composition:
type Logger struct {
*slog.Logger
}
type Server struct {
Logger
addr string
}
// s.Info(...) は機能します — Logger を通じて slog.Logger から昇格
s := Server{Logger: Logger{slog.Default()}, addr: ":8080"}
s.Info("starting", "addr", s.addr)
昇格されたメソッドのレシーバは内部型で、外部型ではありません。外部型は同じ名前で独自のメソッドを定義することで上書きできます。
いつ埋め込むか vs 名前付きフィールド
| 使用 | 場合 |
|---|---|
| 埋め込み | 内部型の完全な API を昇格させたい場合 — 外部型は「enhanced version である」 |
| 名前付きフィールド | 内部型が内部でのみ必要な場合 — 外部型は「依存関係を持つ」 |
// 埋め込み — Server は すべての http.Handler メソッドを公開
type Server struct {
http.Handler
}
// 名前付きフィールド — Server は store を使用しますが、メソッドは公開しません
type Server struct {
store *DataStore
}
インターフェース経由の依存性注入
依存関係をコンストラクタでインターフェースとして受け入れます。これはコンポーネントをデカップルし、テストを直感的にします:
type UserStore interface {
FindByID(ctx context.Context, id string) (*User, error)
}
type UserService struct {
store UserStore
}
func NewUserService(store UserStore) *UserService {
return &UserService{store: store}
}
テストでは、UserStore を満たすモックまたはスタブを渡します — 実際のデータベースは不要です。
Struct フィールドタグ
シリアライゼーションコントロール用フィールドタグを使用します。シリアライズされた struct のエクスポートされたフィールドはフィールドタグを持つ必要があります:
type Order struct {
ID string `json:"id" db:"id"`
UserID string `json:"user_id" db:"user_id"`
Total float64 `json:"total" db:"total"`
Items []Item `json:"items" db:"-"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
DeletedAt time.Time `json:"-" db:"deleted_at"`
Internal string `json:"-" db:"-"`
}
| ディレクティブ | 意味 |
|---|---|
json:"name" | JSON 出力のフィールド名 |
json:"name,omitempty" | ゼロ値の場合はフィールドを省略 |
json:"-" | JSON から常に除外 |
json:",string" | JSON 文字列として数値/ブール値をエンコード |
db:"column" | データベースカラムマッピング (sqlx など) |
yaml:"name" | YAML フィールド名 |
xml:"name,attr" | XML 属性 |
validate:"required" | Struct バリデーション (go-playground/validator) |
ポインタ vs 値レシーバ
ポインタを使用 (s *Server) | 値を使用 (s Server) |
|---|---|
| メソッドがレシーバを変更 | レシーバは小さく不変 |
レシーバが sync.Mutex などを含む | レシーバは基本型 (int、string) |
| レシーバは大きな struct | メソッドは読み取り専用アクセッサ |
| 一貫性:1 つのメソッドがポインタを使用する場合、すべてが使用する必要があります | マップと関数値 (すでに参照型) |
レシーバ型は型のすべてのメソッドで一貫している必要があります — 1 つのメソッドがポインタレシーバを使用する場合、すべてのメソッドが使用する必要があります。
noCopy で Struct コピーを防ぐ
一部の struct は最初の使用後にコピーされてはいけません (mutex、チャネル、または内部ポインタを含むなど)。noCopy センチネルを埋め込み、go vet が偶発的なコピーをキャッチするようにします:
// noCopy は最初の使用後にコピーされてはいけない struct に追加される可能性があります。
// https://pkg.go.dev/sync#noCopy を参照
type noCopy struct{}
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
type ConnPool struct {
noCopy noCopy
mu sync.Mutex
conns []*Conn
}
go vet は ConnPool 値がコピーされた場合 (値で渡される、割り当てられるなど)、エラーを報告します。これは標準ライブラリが sync.WaitGroup、sync.Mutex、strings.Builder などで使用する同じ技術です。
これらの struct は常にポインタで渡します:
// 良い
func process(pool *ConnPool) { ... }
// 悪い — go vet がこれをフラグします
func process(pool ConnPool) { ... }
クロスリファレンス
- → インターフェース命名規則 (Reader、Closer、Stringer) については
samber/cc-skills-golang@golang-namingスキルを参照してください。 - → 関数型オプション、コンストラクタ、ビルダーパターンについては
samber/cc-skills-golang@golang-design-patternsスキルを参照してください。 - → インターフェースを使用した DI パターンについては
samber/cc-skills-golang@golang-dependency-injectionスキルを参照してください。 - → 値 vs ポインタ関数パラメータについては
samber/cc-skills-golang@golang-code-styleスキルを参照してください (レシーバとは異なります)。
よくある間違い
| 間違い | 修正 |
|---|---|
| 大きなインターフェース (5 個以上のメソッド) | フォーカスした 1~3 個のメソッドインターフェースに分割し、必要に応じて構成します。 |
| インターフェースを実装者パッケージで定義 | 消費される場所で定義します。 |
| コンストラクタからインターフェースを返す | コンクリート型を返します。 |
| カンマ ok なしの型アサーション | 常に v, ok := x.(T) を使用します。 |
| いくつかのメソッドのみが必要な場合の埋め込み | 名前付きフィールドを使用し、明示的にデリゲートします。 |
| シリアライズされた struct のフィールドタグなし | マーシャルされた型のすべてのエクスポートされたフィールドにタグを付けます。 |
| 型のポインタと値レシーバの混合 | 1 つを選択して一貫性を保ちます。 |
| コンパイル時インターフェースチェックを忘れる | var _ Interface = (*Type)(nil) を追加します。 |
String() の代わりに ToString() を使用 | 標準的なメソッド名を守ります。 |
| 単一の実装を持つ性急なインターフェース | 具体型で始め、後で必要になったら抽出します。 |
| ゼロ値 struct の nil マップ/スライス | メソッドで遅延初期化を使用します。 |
型安全な操作に any を使用 | ジェネリクス ([T comparable]) を代わりに使用します。 |
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- samber
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/samber/cc-skills-golang / ライセンス: MIT
関連スキル
nano-banana-2
inference.sh CLIを通じてGoogle Gemini 3.1 Flash Image Preview(Nano Banana 2)で画像を生成します。テキストから画像を生成する機能、画像編集、最大14枚の複数画像入力、Google Searchグラウンディング機能に対応しています。トリガーワード:「nano banana 2」「nanobanana 2」「gemini 3.1 flash image」「gemini 3 1 flash image preview」「google image generation」
octocode-slides
洗練されたマルチファイル形式のHTMLプレゼンテーションを生成します。6段階のフロー(概要 → リサーチ → アウトライン → デザイン → 実装 → レビュー)で構成されています。各スライドは独立したHTMLファイルとなり、iframeで読み込まれます。「スライドを作成してほしい」「プレゼンテーションを作ってほしい」「HTMLスライドを生成してほしい」「デックを構築してほしい」といった依頼や、ノート・ドキュメント・コードを洗練されたプレゼンテーションに変換する際に使用できます。
gpt-image2-ppt
OpenAIのgpt-image-2を使用して、視覚的に優れたPPTスライドを生成します。Spatial Glass、Tech Blue、Editorial Monoなど10種類のキュレーション済みスタイルに対応し、ユーザーが提供したPPTXファイルを模倣するテンプレートクローンモードも搭載しています。HTMLビューアと16:9形式のPPTXファイルを出力します。プレゼンテーション、スライド、ピッチデック、投資家向けPPT、雑誌風PPTの作成依頼などで活用してください。
nano-banana
Nano Banana PRO(Gemini 3 Pro Image)およびNano Banana(Gemini 2.5 Flash Image)を使用したAI画像生成機能です。以下の場合に活用できます:(1)テキストプロンプトからの画像生成、(2)既存画像の編集、(3)インフォグラフィックス、ロゴ、商品写真、ステッカーなどのプロフェッショナルなビジュアルアセット制作、(4)複数画像での人物キャラクターの一貫性保持、(5)正確なテキスト描画を含む画像生成、(6)AI生成ビジュアルが必要なあらゆるタスク。「画像を生成」「画像を作成」「写真を作る」「ロゴをデザイン」「インフォグラフィックスを作成」「AI画像」「nano banana」またはその他の画像生成リクエストをトリガーとして機能します。
oiloil-ui-ux-guide
モダンでクリーンなUI/UXガイダンス・レビュースキルです。新機能や既存システム(Webアプリ)に対して、実行可能なUI/UX改善提案、デザイン原則、デザインレビューチェックリストが必要な場合に活用できます。CRAP(コントラスト・反復・配置・近接)をベースに、タスクファーストなUX、情報設計、フィードバック・システムステータス、一貫性、affordances、エラー防止・復旧、認知負荷を重視します。モダンミニマルスタイル(クリーン・余白・タイポグラフィ主導)を強制し、不要なテキストを削減、アイコンとしての絵文字を禁止し、統一されたアイコンセットから直感的で洗練されたアイコンを推奨します。
axiom-hig-ref
Apple Human Interface Guidelines リファレンス — 色(セマンティックカラー、カスタムカラー、パターン)、背景(マテリアル階層、ダイナミック背景)、タイポグラフィ(標準スタイル、カスタムフォント、Dynamic Type)、SF Symbols(レンダリングモード、色、多言語対応)、ダークモード、アクセシビリティ、プラットフォーム固有の考慮事項を網羅したガイドラインです。