質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • Go

    650questions

    Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

  • デザインパターン

    80questions

    デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

Goで抽象クラスありのObserverパターンを実装したい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 571

cloudspider

score 80

Observerパターンの勉強をしているのですが、言語的なこだわりはあまりないので練習がてら、最近始めたGoで書いてみようと思いました。

PHPで解説されたこのページのコードをGoに書き直そうとしているのですが、うまくいきません。
上記のコードの最初の「実装コード」の部分です。
簡単のために、idやtimeなど一部省略したり、具象クラスもGirlFriendのみにしています。

package main

import "fmt"

// =======================
// 抽象クラス Subject
// =======================

type Subject struct {
    observers []Observer
}

func NewSubject() *Subject {
    return &Subject{observers: []Observer{}}
}

func (self *Subject) registerObser(observer Observer) {
    self.observers = append(self.observers, observer)
}

// func removeObserver

// func (self *Subject) notify() {}

// =======================
// 具象クラス SMS extends Subject
// =======================

type SMS struct {
    *Subject
}

func NewSMS() *SMS {
    return &SMS{Subject: NewSubject()}
}

func (self *SMS) notifyObservers() {
    for _, observer := range self.observers {
        observer.notify()
    }
}

func (self *SMS) iAmHome() {
    self.notifyObservers()
}

// =======================
// 抽象クラス Observer
// =======================

type IObserver interface {
    notify()
}

type Observer struct {
    id      int
    subject SMS
    IObserver
}

// constructor
func NewObserver(id int, subject SMS) *Observer {
    // subject.registerObser()
    return &Observer{id: id, subject: subject}
}

// =======================
// 具象クラス GirlFriend,Mum  extends Observer
// =======================

type GirlFriend struct {
    *Observer
}

func NewGirlFriend(id int, subject SMS) *GirlFriend {
    return &GirlFriend{Observer: NewObserver(id, subject)}
}

func (self *GirlFriend) notify(time string) {
    fmt.Printf("今帰ったよ %v", time)
}

// =======================
// main
// =======================

func main() {

    sms := NewSMS()
    NewGirlFriend(2, *sms)

    sms.iAmHome()
}

Goはオブジェクト指向の言語ではない(?)ので、上のコードはGoらしくないコードになっているかと思います。

私が知りたいのは、上のコードを動かす書き方と、上記のサイトの内容を同じ程度の柔軟性をもったGoの書き方です。

オブジェクト指向もObserverパターンもGo言語自体も詳しく知らない状態です。

ツッコミどころは沢山あるかと思いますが、どなたかご存知の方がいらっしゃればご教示願えませんでしょうか。
よろしくおねがいします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

wikipedia:Observer パターンを参考にGoで実装した例を以下に示します。

https://play.golang.org/p/lwNkVd2nzyg

package main

import (
    "fmt"
    "sync"
)

type Param string

type Observer interface {
    Notify(p Param)
}

type Subject struct {
    mu        sync.RWMutex
    observers []Observer
}

func (nb *Subject) AddObserver(o Observer) {
    nb.mu.Lock()
    defer nb.mu.Unlock()
    nb.observers = append(nb.observers, o)
}

func (nb *Subject) DeleteObserver(o Observer) {
    nb.mu.Lock()
    defer nb.mu.Unlock()
    obs := nb.observers[0:0]
    for _, v := range nb.observers {
        if v != o {
            obs = append(obs, v)
        }
    }
    nb.observers = obs
}

func (nb *Subject) Notify(p Param) {
    nb.mu.RLock()
    defer nb.mu.RUnlock()
    for _, v := range nb.observers {
        v.Notify(p)
    }
}

type Something struct {
    Subject
}

type Observer1 struct{}

func (o1 *Observer1) Notify(p Param) {
    fmt.Println("obs1:", p)
}

type Observer2 struct{}

func (o1 *Observer2) Notify(p Param) {
    fmt.Println("obs2:", p)
}

func main() {
    s := &Something{}
    ob1 := &Observer1{}
    ob2 := &Observer2{}
    s.AddObserver(ob1)
    s.AddObserver(ob2)
    s.Notify("hello!")
    s.DeleteObserver(ob1)
    s.Notify("World!")
}

Observerになれるのは「Notify(p Param)」というメソッドを持つというのが条件です。
Subjectにsync.RWMutexがあるのは複数のgoroutineから操作されても大丈夫なようにするためです。
(蛇足かもしれませんが、この例ではSubjectも「Notify(p Param)」というメソッドを持っているのでObserverになれます。例えば通知にツリー構造を構築することもできます。)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

同じタグがついた質問を見る

  • Go

    650questions

    Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

  • デザインパターン

    80questions

    デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。