# 观察者模式

**观察者模式(Observer Pattern)**：定义对象之间的一种一对多依赖关系，让多个观察者对象监听主题对象，当主题对象发生变化时，其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅（Publish/Subscribe）模式、模型-视图（Model/View）模式、源-监听器（Source/Listener）模式或从属者（Dependents）模式。观察者模式是一种对象行为型模式。

## 适用场景

* 关联行为场景，建立一套触发机制

## 优点

* 观察者和被观察者之间建立一个抽象的耦合
* 观察者模式支持广播通信

## 缺点

* 观察者之间有过多的细节依赖，提高时间消耗以及程序复杂度
* 使用要得当，要避免循环调用

在日常生活中，我们有很多这样的使用示例。例如我订阅了某个公众号，当公众号进行了更新之后，所有的订阅者都能收到消息。我对朋友圈的某条动态进行了评论，当有新评论增加时，我也会收到相应的动态更新。

## Golang Demo

首先在这里说明一下，go 中并没有像java 那样，从编程语言层面对设计模式进行支持。因此我们主要还是从代码语义的角度上来进行理解。如果想要严格的理解观察者模式，建议可以结合实际应用场景查看一下java的Demo。

```go
package observer

type Observer interface {
  Update(observable *Observable)
}

type Observable struct {
  content string
  obs     []Observer
}

func NewObservable() *Observable {
  return &Observable{obs: make([]Observer, 0)}
}

//AddObserver 向被观察者的订阅集合中添加观察者
//需要是线程安全的
//如果被观察者已经存在，则不添加
func (o *Observable) AddObserver(observer Observer) {
  o.obs = append(o.obs, observer)
}

func (o *Observable) Notify() {
  for _, observer := range o.obs {
    observer.Update(o)
  }
}

// 更新被订阅者的内容
func (o *Observable) UpdateContent(content string) {
  o.content = content

}
```

```go
package observer

import "fmt"

type Subscriber struct {
  name string
}

func NewSubscriber(name string) *Subscriber {
  return &Subscriber{name: name}
}

func (s *Subscriber) Update(observable *Observable) {
  fmt.Printf("%s  receive %s \n", s.name, observable.content)
}
```

```go
package observer

func ExampleObserver() {
  observable := NewObservable()
  subscriber1 := NewSubscriber("hello1")

  subscriber2 := NewSubscriber("hello2")
  observable.AddObserver(subscriber1)
  observable.AddObserver(subscriber2)
  observable.UpdateContent("world")
  observable.Notify()
  // Output:
  // hello1  receive worl1d
  // hello2  receive world
}
```

## Java Demo

首先定义一个 公众号的类。

```java
package tech.selinux.design.pattern.behavioral.observer;

import java.util.Observable;

/** 公众号 */
public class OfficialAccounts extends Observable {
  private String accountsName;

  public OfficialAccounts(String accountsName) {
    this.accountsName = accountsName;
  }

  public String getAccountsName() {
    return accountsName;
  }

  // 代表状态发生改变
  public void publishArticle(OfficialAccounts accounts, Article article) {
    System.out.println(article.getTitle() + "  on   " + accounts.getAccountsName());
    setChanged();
    notifyObservers(article);
  }
}
```

公众号内的文章是依附于公众号存在的。

```java
package tech.selinux.design.pattern.behavioral.observer;

public class Article {
  private String title;
  private String content;

  public String getContent() {
    return content;
  }

  public void setContent(String content) {
    this.content = content;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }
}
```

接下来定义用户，用户是观察者，公众号是被观察者。

```java
package tech.selinux.design.pattern.behavioral.observer;

import java.util.Observable;
import java.util.Observer;

/** 对于用户来说，观察的是公众号 观察者是 user，被观察者是 offical accounts */
public class User implements Observer {
  private String nickName;

  public User(String nickName) {
    this.nickName = nickName;
  }

  @Override
  public void update(Observable o, Object arg) {
    OfficialAccounts accounts = (OfficialAccounts) o;
    Article article = (Article) arg;
    StringBuilder sb = new StringBuilder();
    sb.append("用户名：")
        .append(nickName)
        .append(": \n")
        .append("公众号 :")
        .append(accounts.getAccountsName())
        .append("\n")
        .append("文章：")
        .append(article.getTitle())
        .append("\n");
    System.out.println(sb.toString());
  }
}
```

```java
package tech.selinux.design.pattern.behavioral.observer;

public class Test {
  public static void main(String[] args) {
    OfficialAccounts accounts = new OfficialAccounts("Linux 中国");

    User user = new User("PegasusMeteor");
    accounts.addObserver(user);

    User user1 = new User("Pegasus");
    accounts.addObserver(user1);

    Article article = new Article();
    article.setTitle("Linux 未来趋势");
    article.setContent("一片大好");
    accounts.publishArticle(accounts, article);
  }
}
```

## UML

![观察者模式UML](/files/-Lfs6o2oKAmwu3GmRT5-)

### 补充另一个版本的Java/Scala Demo 以及源码解析

## Java Demo\_

## Scala Demo

## UML\_

## 源码解析


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.selinux.tech/designpattern/behavioral-pattern/observer-pattern.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
