こんにちは、KOUKIです。
この記事では、デザインパターンの一つであるMediatorパターンについて紹介します。
<目次>
デザインパターンまとめ
シチュエーション
Mediator は、「仲裁人」を意味する英単語です。
Mediator パターンは、多数のオブジェクト(Go言語ではインスタンス)の間の調整を行いつつ、特定の処理を進める必要がある場合に適したパターンです。
例えば、チャットを例にあげましょう。
一般的なチャットでは、ユーザー間(多数のオブジェクト)にコネクション(調整)を貼り、その間でメッセージのやりとり(特定の処理)を行います。
Mediatorパターンのサンプルに適していそうなので、実装してみましょう。
Mediatorパターンの適用
簡単なチャットプログラムを実装します。
User構造体
User構造体を実装します。
1 2 3 4 5 6 7 8 9 10 11 |
package main type User struct { Name string chatRoom *ChatRoom // あとで実装 chatLog []string } func NewUser(name string) *User { return &User{Name: name} } |
User構造体のメソッド
次に、User構造体のメソッドを実装します。
1 2 3 4 5 6 7 8 9 10 11 |
// メッセージを受信 func (u *User) ReceiveMsg(sender, msg string) { s := fmt.Sprintf("%s: '%s'", sender, msg) fmt.Printf("[%s's chat session] %s\n", u.Name, s) u.chatLog = append(u.chatLog, s) } // コネクトユーザー全てにメッセージを送る func (u *User) Say(msg string) { u.chatRoom.Broadcast(u.Name, msg) // あとで実装 } |
ChatRoom構造体
次に、ChatRoom構造体を実装します。これが、Mediatorになります。
1 2 3 4 5 6 7 |
type ChatRoom struct { users []*User } func NewChatRoom() *ChatRoom { return &ChatRoom{} } |
ChatRoom構造体のメソッド
ChatRoom構造体のメソッドを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
func (c *ChatRoom) Broadcast(sender, msg string) { for _, u := range c.users { if u.Name != sender { u.ReceiveMsg(sender, msg) } } } // コネクションを貼る func (c *ChatRoom) Login(u *User) { joinMsg := u.Name + " log in the chat" c.Broadcast("ChartRoom", joinMsg) u.chatRoom = c c.users = append(c.users, u) } |
使ってみよう
ここまで実装したサンプルプログラムを使ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func main() { chatRoom := NewChatRoom() // コネクションを貼る harry := NewUser("Harry") chatRoom.Login(harry) ron := NewUser("Ron") chatRoom.Login(ron) // 特定の処理 harry.Say("Hi, Ron!") ron.Say("Hi, Harry! What's happen!") |
1 2 3 4 5 6 7 |
$ go run main.go // RonがログインしたのでHarryにメッセージが送られた [Harry's chat session] ChartRoom: 'Ron log in the chat // メッセージを送り合う [Ron's chat session] Harry: 'Hi, Ron!' [Harry's chat session] Ron: 'Hi, Harry! What's happen!' |
OKですね。
ChatRoom(Mediator)を介して、Userインスタンス同士がメッセージのやりとりをできるようになりました。
機能の追加
プライベートなメッセージを送れるようしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
// プライベートなメッセージを送る func (u *User) DirectMsg(who, msg string) { u.chatRoom.Message(u.Name, who, msg) } func (c *ChatRoom) Message(src, dst, msg string) { for _, u := range c.users { if u.Name == dst { u.ReceiveMsg(src, msg) } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ go run main.go [Harry's chat session] ChartRoom: 'Ron log in the chat' [Ron's chat session] Harry: 'Hi, Ron!' [Harry's chat session] Ron: 'Hi, Harry! What's happen!' [Harry's chat session] ChartRoom: 'Hermione's log in the chat' [Ron's chat session] ChartRoom: 'Hermione's log in the chat' // Hermione'sがログインした [Harry's chat session] Hermione's: 'I'm Join!' [Ron's chat session] Hermione's: 'I'm Join!' // Harryには届かず、Ronにだけメッセージが送られた [Hermione's's chat session] Ron: 'I love you.' |
なかなか面白いです。
まとめ
個人的に、Mediatorパターンを実務で使用するイメージがあまり湧きませんでした。
まだ慣れていないだけかもしれませんが^^;
時間があれば、本記事で紹介したチャットプログラムとは別のサンプルも紹介したいと思います!
それでは、また!
Go言語まとめ
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
package main import "fmt" type User struct { Name string chatRoom *ChatRoom chatLog []string } func NewUser(name string) *User { return &User{Name: name} } // メッセージを受信 func (u *User) ReceiveMsg(sender, msg string) { s := fmt.Sprintf("%s: '%s'", sender, msg) fmt.Printf("[%s's chat session] %s\n", u.Name, s) u.chatLog = append(u.chatLog, s) } // コネクトユーザー全てにメッセージを送る func (u *User) Say(msg string) { u.chatRoom.Broadcast(u.Name, msg) } // プライベートなメッセージを送る func (u *User) DirectMsg(who, msg string) { u.chatRoom.Message(u.Name, who, msg) } type ChatRoom struct { users []*User } func NewChatRoom() *ChatRoom { return &ChatRoom{} } func (c *ChatRoom) Broadcast(sender, msg string) { for _, u := range c.users { if u.Name != sender { u.ReceiveMsg(sender, msg) } } } // コネクションを貼る func (c *ChatRoom) Login(u *User) { joinMsg := u.Name + " log in the chat" c.Broadcast("ChartRoom", joinMsg) u.chatRoom = c c.users = append(c.users, u) } func (c *ChatRoom) Message(src, dst, msg string) { for _, u := range c.users { if u.Name == dst { u.ReceiveMsg(src, msg) } } } func main() { chatRoom := NewChatRoom() // コネクションを貼る harry := NewUser("Harry") chatRoom.Login(harry) ron := NewUser("Ron") chatRoom.Login(ron) // 特定の処理 harry.Say("Hi, Ron!") ron.Say("Hi, Harry! What's happen!") // 追加 hermiones := NewUser("Hermione's") chatRoom.Login(hermiones) hermiones.Say("I'm Join!") ron.DirectMsg(hermiones.Name, "I love you.") } |
別パターンのChatプログラム
本記事で説明したプログラムより、少し簡略化したChatプログラムも載せておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
package main import "fmt" type Member struct { name string chatRoom *ChatRoom } func NewMember(name string) *Member { return &Member{name: name} } func (m *Member) send(toMember *Member, msg string) { m.chatRoom.send(toMember, m, msg) } func (m *Member) receive(fromMember *Member, msg string) { s := fmt.Sprintf("%s to %s: %s", m.name, fromMember.name, msg) fmt.Println(s) } // Mediator type ChatRoom struct { members []*Member } func NewChatRoom() *ChatRoom { return &ChatRoom{} } func (c *ChatRoom) addMember(member *Member) { fmt.Println(fmt.Sprintf("%s Join!", member.name)) c.members = append(c.members, member) member.chatRoom = c } func (c *ChatRoom) send(fromMember, toMember *Member, msg string) { toMember.receive(fromMember, msg) } func main() { chat := NewChatRoom() harry := NewMember("Harry") ron := NewMember("Ron") hermiones := NewMember("Hermione's") chat.addMember(harry) chat.addMember(ron) chat.addMember(hermiones) fmt.Println("---------") harry.send(ron, "Hey, Ron") ron.send(harry, "What's up, Harry") hermiones.send(ron, "Ron, are you ok?") } |
1 2 3 4 5 6 7 8 |
$ go run main.go Harry Join! Ron Join! Hermione's Join! --------- Harry to Ron: Hey, Ron Ron to Harry: What's up, Harry Hermione's to Ron: Ron, are you ok? |
コメントを残す
コメントを投稿するにはログインしてください。