埋め込まれたメソッドのシャドウイング
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!)"
}
シャドウイングを使用すると、明示的な修飾を通じてオリジナルへのアクセスを維持しながら、どの振る舞いを公開するかを制御できます。
チャレンジ
簡単異なる通知チャネルがメッセージのフォーマットと配信方法をカスタマイズできる通知システムを構築しましょう。メソッドのシャドウイング(method shadowing)を使用して、特定のチャネルがデフォルトの動作をオーバーライドできるようにしつつ、必要に応じて元の実装にもアクセスできるようにします。
コードは以下の3つのファイルに分けて構成します:
base.go:TitleとPriorityフィールド(共に文字列)を持つ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: 入力から通知の詳細を読み取り、両方の型の通知を作成し、以下を出力することでシャドウイングを実演します:EmailNotificationのフォーマット済み出力(シャドウイングされたメソッドを使用)EmailNotificationのベースフォーマット(埋め込まれたメソッドを明示的に呼び出す)SlackNotificationのフォーマット済み出力(内部でベースメソッドを使用)
以下の入力が提供されます:
- 1行目: 通知のタイトル
- 2行目: 優先度レベル
- 3行目: メールの受信者
- 4行目: Slackのチャネル名
例えば、Server Alert、HIGH、admin@company.com、ops-alerts が与えられた場合、出力は以下のようになります:
Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server AlertEmailNotification がベースのフォーマットを完全に置き換えているのに対し、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() // プレースホルダー - 実装時に削除する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー