Menu
Coddy logo textTech

埋め込み vs 集約

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 38/107。

embedding によってフィールドとメソッドが外側の構造体に昇格することを学びました。しかし Go は、別の構造体を名前付きフィールドとして含める aggregation もサポートしています。それぞれのアプローチをいつ使用するかを理解することが、優れた設計の鍵です。

embeddingでは、フィールド名を省略し、内部型のメンバーに直接アクセスできるようになります。

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // 埋め込み - フィールド名なし
}

d := Document{}
d.Write()  // 直接アクセス

aggregationでは、field に explicit な名前を付け、その名前で内側の型に accessする必要があります。

type Document struct {
    writer Writer  // 集約 - 名前付きフィールド
}

d := Document{writer: Writer{}}
d.writer.Write()  // フィールド名を使用する必要がある

主な違いは、表現している関係です。埋め込みは、外側の型が内側の型のアイデンティティを引き継ぐ「is-a」関係を示します。集約は、外側の型が内側の型を単に含む「has-a」関係を示します。

内側の型によって実装されたインターフェースを外側の型が満たすようにしたい場合や、directなメソッドアクセスによって API をより簡潔にしたい場合は、embeddingを使用します。内側の型が公開すべきでないimplementation detailである場合や、内側の型へのexplicitなアクセス方法を制御したい場合は、aggregationを使用します。

challenge icon

チャレンジ

簡単

embedding と aggregation のどちらを使用すべきかを示すレポート生成システムを構築しましょう。一部のコンポーネントには直接 access できるようにし(embedding)、その他のコンポーネントは internal implementation detail として保持する(aggregation)システムを作成します。

コードを次の3つのファイルに整理します。

  • formatter.go: Style field(string)と、style でテキストを囲んだ結果([Style] text [/Style] のような形式)を返す Format(text string) string method を持つ Formatter struct を Create します。これは直接 expose すべきではない internal helper です。
  • report.go: レポート生成を異なる方法で処理する2つの struct を Create します。
    • AuthorDate の fields を持つ Metadata struct。これは直接 access できるべき public information を表します。
    • Title field を持つ Report struct。Metadataembeds(Author と Date に直接 access するため)し、Formatter にはaggregationを使用します(named field formatter を持つ internal implementation detail として)。
    ReportGenerate() string method を Add します。この method は internal formatter を使用して title を format し、metadata を含めます。
  • main.go: input から report details を読み取り、embedded metadata と aggregated formatter の両方を持つ Report を Create します。embedding によって report 上の Author に直接 access し、その後、hidden formatter を internal に使用して formatted report を Generate することで、その違いを示します。

次の input が提供されます。

  • Line 1: Report title
  • Line 2: Author name
  • Line 3: Date
  • Line 4: Formatter style

Format method は次を return する必要があります。

[Style] text [/Style]

Generate method は次を return する必要があります。

[formatted title]
Author: [Author], Date: [Date]

main file では、3行を print します。

  1. Report 上で直接 access した Author(embedding によって field が promote されることを demonstrates)
  2. embedded type name(report.Metadata.Date)を通じて access した Date
  3. Generate() を calling した結果

たとえば、Quarterly ResultsJane Doe2024-01-15BOLD が与えられた場合、output は次のようになります。

Jane Doe
2024-01-15
[BOLD] Quarterly Results [/BOLD]
Author: Jane Doe, Date: 2024-01-15

AuthorDate は embedding によって Report 上で直接 accessible である一方、formatter は hidden のままであることに注目してください。formatter は named field として aggregated されているため、report.Format() を直接 calling することはできません。これは実際の "is-a""has-a" の relationship を demonstrates しています。

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	title := scanner.Text()
	
	scanner.Scan()
	author := scanner.Text()
	
	scanner.Scan()
	date := scanner.Text()
	
	scanner.Scan()
	style := scanner.Text()
	
	// TODO: author と date を持つ Metadata 構造体を作成する
	
	// TODO: 指定された style で Formatter を作成する
	
	// TODO: Metadata を埋め込み、Formatter を集約する Report を作成する
	
	// TODO: Report 上で直接アクセスした author を出力する(埋め込みによりフィールドが昇格する)
	
	// TODO: 埋め込み型名を通じてアクセスした date を出力する (report.Metadata.Date)
	
	// TODO: Generate() を呼び出した結果を出力する
	
	// 未使用変数の警告を抑制する(変数を使用したらこれらを削除する)
	_ = title
	_ = author
	_ = date
	_ = style
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ