Menu
Coddy logo textTech

埋め込まれたメソッドのシャドウイング

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

埋め込み型がすでに持っているメソッドに対して、外側の構造体が独自の実装を提供する必要がある場合があります。これはシャドウイングと呼ばれ、昇格されたメソッドを独自の振る舞いでオーバーライドすることができます。

埋め込みメソッドと同じ名前のメソッドを外側の構造体で定義すると、外側のメソッドが優先されます:

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "Some sound"
}

type Dog struct {
    Animal
    Breed string
}

func (d Dog) Speak() string {
    return "Woof!"
}

Dog に対して Speak() を呼び出すと、外側のメソッドが使用されます:

func main() {
    d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
    
    fmt.Println(d.Speak())        // Woof! - Dog のメソッド
    fmt.Println(d.Animal.Speak()) // Some sound - Animal のメソッド
}

埋め込まれたメソッドはなくなったわけではありません。埋め込まれた型名を通じて、明示的にアクセスすることができます。このパターンは、元の実装を利用可能な状態に保ちつつ、振る舞いをカスタマイズしたい場合に便利です。おそらく、オーバーライドするメソッド内から元のメソッドを呼び出すために使用されます。

func (d Dog) Speak() string {
    return d.Animal.Speak() + " (but actually Woof!)"
}

シャドウイングを使用すると、明示的な修飾を通じてオリジナルへのアクセスを維持しながら、どの振る舞いを公開するかを制御できます。

challenge icon

チャレンジ

簡単

異なる通知チャネルがメッセージのフォーマットと配信方法をカスタマイズできる通知システムを構築しましょう。メソッドのシャドウイング(method shadowing)を使用して、特定のチャネルがデフォルトの動作をオーバーライドできるようにしつつ、必要に応じて元の実装にもアクセスできるようにします。

コードは以下の3つのファイルに分けて構成します:

  • base.go: TitlePriority フィールド(共に文字列)を持つ BaseNotification 構造体を作成します。これに、通知を標準形式 [Priority] Title で返す Format() string メソッドを実装してください。
  • channels.go: BaseNotification を埋め込み(embed)、Format メソッドを独自の実装でシャドウイングする2つの通知チャネル型を作成します:
    • Recipient フィールドを持つ EmailNotification — その Format()Email to [Recipient]: [Priority] Title を返すようにします。
    • Channel フィールドを持つ SlackNotification — その Format() は、埋め込まれた BaseNotification.Format() メソッドを呼び出し、その結果の先頭に #[Channel]: を付加するようにします。
  • main.go: 入力から通知の詳細を読み取り、両方の型の通知を作成し、以下を出力することでシャドウイングを実演します:
    1. EmailNotification のフォーマット済み出力(シャドウイングされたメソッドを使用)
    2. EmailNotification のベースフォーマット(埋め込まれたメソッドを明示的に呼び出す)
    3. SlackNotification のフォーマット済み出力(内部でベースメソッドを使用)

以下の入力が提供されます:

  • 1行目: 通知のタイトル
  • 2行目: 優先度レベル
  • 3行目: メールの受信者
  • 4行目: Slackのチャネル名

例えば、Server AlertHIGHadmin@company.comops-alerts が与えられた場合、出力は以下のようになります:

Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server Alert

EmailNotification がベースのフォーマットを完全に置き換えているのに対し、SlackNotification は元のメソッドを呼び出してチャネルのコンテキストを追加することで機能を強化している点に注目してください。どちらのアプローチも、メソッドのシャドウイングを効果的に使用する異なる方法を示しています。

チートシート

埋め込み型のメソッドと同じ名前のメソッドを外部構造体で定義すると、外部メソッドが埋め込みメソッドをシャドウイング(オーバーライド)します。

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "Some sound"
}

type Dog struct {
    Animal
    Breed string
}

func (d Dog) Speak() string {
    return "Woof!"
}

外部構造体でメソッドを呼び出すと、外部メソッドが優先されます:

d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}

fmt.Println(d.Speak())        // Woof! - Dogのメソッド
fmt.Println(d.Animal.Speak()) // Some sound - Animalのメソッド

埋め込みメソッドは、埋め込み型名を使用して明示的に指定することで、引き続きアクセス可能です。オーバーライドするメソッド内から元の実装を呼び出すことができます:

func (d Dog) Speak() string {
    return d.Animal.Speak() + " (but actually Woof!)"
}

自分で試してみよう

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 通知のタイトルを読み込む
	scanner.Scan()
	title := scanner.Text()
	
	// 優先度レベルを読み込む
	scanner.Scan()
	priority := scanner.Text()
	
	// メールの受信者を読み込む
	scanner.Scan()
	recipient := scanner.Text()
	
	// Slackチャンネル名を読み込む
	scanner.Scan()
	channel := scanner.Text()
	
	// TODO: 基本通知、タイトル、優先度、および受信者を使用してEmailNotificationを作成する
	
	// TODO: 基本通知、タイトル、優先度、およびチャンネルを使用してSlackNotificationを作成する
	
	// TODO: EmailNotificationのフォーマット済み出力を出力する(シャドウイングされたメソッドを使用)
	
	// TODO: EmailNotificationの基本フォーマットを出力する(埋め込みメソッドを明示的に呼び出す)
	
	// TODO: SlackNotificationのフォーマット済み出力を出力する
	
	fmt.Println() // プレースホルダー - 実装時に削除する
}
quiz icon腕試し

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

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