Menu
Coddy logo textTech

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

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

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

外側の構造体で embedded method と同じ名前の method を定義すると、外側の method が優先されます:

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

チャレンジ

簡単

異なる notification channel がメッセージの format と配信方法をカスタマイズできる notification system を構築しましょう。method shadowing を使って、特定の channel がデフォルトの動作をオーバーライドしつつ、必要なときには元の実装にもアクセスできるようにします。

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

  • base.go: TitlePriority の fields(どちらも文字列)を持つ BaseNotification struct を作成します。notification を標準的な format で返す Format() string method を定義します: [Priority] Title
  • channels.go: BaseNotification を embed し、独自の実装で Format method を shadow する2つの notification channel 型を作成します。
    • EmailNotification: Recipient field を持ちます。その Format()Email to [Recipient]: [Priority] Title を返します。
    • SlackNotification: Channel field を持ちます。その Format() は埋め込まれた BaseNotification.Format() method を呼び出し、結果の先頭に #[Channel]: を付加します。
  • main.go: 入力から notification の詳細を読み取り、両方の型の notification を作成し、次の内容を出力して shadowing を示します。
    1. EmailNotification の formatted output(その shadow された method を使用)
    2. EmailNotification の base format(埋め込まれた method を explicitly に呼び出す)
    3. SlackNotification の formatted output(内部で base method を使用)

次の入力が提供されます。

  • 1行目: notification のタイトル
  • 2行目: Priority level
  • 3行目: Email recipient
  • 4行目: Slack channel name

たとえば、Server AlertHIGHadmin@company.comops-alerts が与えられた場合、output は次のようになります。

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

EmailNotification は base の format を完全に置き換える一方、SlackNotification は元の method を呼び出して channel の context を追加することで拡張していることに注目してください。どちらのアプローチも、method shadowing を効果的に使用する異なる方法を示しています。

自分で試してみよう

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: ベース通知、title、priority、recipientを使ってEmailNotificationを作成する
	
	// TODO: ベース通知、title、priority、channelを使ってSlackNotificationを作成する
	
	// TODO: EmailNotificationのフォーマット済み出力を表示する(シャドウされたメソッドを使用)
	
	// TODO: EmailNotificationのベースフォーマットを表示する(埋め込みメソッドを明示的に呼び出す)
	
	// TODO: SlackNotificationのフォーマット済み出力を表示する
	
	fmt.Println() // プレースホルダー - 実装時に削除する
}
quiz icon腕試し

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

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

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