Skip to content

观察者模式

当一个对象状态改变时,自动通知所有依赖它的对象,让它们自动更新

适用的场景

当对象之间是“一对多”关系,比如一人改动,多人知道 当你不想硬编码通知逻辑,让观察者自己决定怎么响应 当你希望事件解耦,让对象低耦合但联动灵活

例如:

  • 游戏中角色属性变化 → UI 自动更新

  • 状态改变 → 多个系统响应

基本结构

  • Subject(发布者):被观察者(发布者)注册、移除、通知观察者

  • Observer(观察者):抽象观察者接口,提供更新方法

  • ConcreteObserver(具体观察者):实际的响应者

  • ConcreteSubject(具体发布者):维护状态并通知变更

示例

csharp
interface IObserver 
{
    void Update(string message);
}

class Fan : IObserver 
{
    private string name;
    public Fan(string name) => this.name = name;
    public void Update(string message) => Console.WriteLine($"{name} 收到了通知:{message}");
}

class Idol 
{
    private List<IObserver> _fans = new();
    public void AddFan(IObserver fan) => _fans.Add(fan);
    public void RemoveFan(IObserver fan) => _fans.Remove(fan);
    public void PostPhoto() 
    {
        foreach (var fan in _fans)
            fan.Update("我发新自拍啦");
    }
}
// 使用:
var idol = new Idol();
idol.AddFan(new Fan("粉丝1号"));
idol.AddFan(new Fan("粉丝2号"));

idol.PostPhoto();

在C#中,你也可以用事件和委托来实现观察者模式

csharp
// 定义委托类型
public delegate void NotificationHandler(string message);

class Fan
{
    private string name;
    public Fan(string name) => this.name = name;
    
    // 这个方法符合NotificationHandler委托的签名
    public void ReceiveNotification(string message) 
        => Console.WriteLine($"{name} 收到了通知:{message}");
}

class Idol
{
    // 定义事件
    public event NotificationHandler OnPostPhoto;
    
    public void PostPhoto()
    {
        // 触发事件
        OnPostPhoto?.Invoke("我发新自拍啦");
    }
}

// 使用:
var idol = new Idol();
var fan1 = new Fan("粉丝1号");
var fan2 = new Fan("粉丝2号");

// 订阅事件
idol.OnPostPhoto += fan1.ReceiveNotification;
idol.OnPostPhoto += fan2.ReceiveNotification;

idol.PostPhoto();

// 取消订阅示例
// idol.OnPostPhoto -= fan1.ReceiveNotification;

GDScript示例

gdscript
class Observer:
    func update(msg: String) -> void:
        pass

class Listener extends Observer:
    var name: String
    
    func _init(name: String):
        self.name = name
        
    func update(msg: String):
        print(name, "收到:", msg)

class ButtonPublisher:
    var observers = []

    func add_observer(o: Observer):
        observers.append(o)

    func click():
        for o in observers:
            o.update("按钮被点击啦!")
            
# 使用:
func _ready():
    var button = ButtonPublisher.new()
    button.add_observer(Listener.new("监听者1号"))
    button.add_observer(Listener.new("监听者2号"))
    button.click()

在GDScript中,你也可以用信号和可调用体来实现观察者模式

gdscript
class Listener:
    var name: String
    
    func _init(name: String):
        self.name = name
        
    func update(msg: String):
        print(name, "收到:", msg)

class ButtonPublisher:
    # 定义信号
    signal button_clicked(message: String)

    func click():
        # 发送信号
        button_clicked.emit("按钮被点击啦!")
            
# 使用:
func _ready():
    var button := ButtonPublisher.new()
    var listener1 := Listener.new("监听者1号")
    var listener2 := Listener.new("监听者2号")
    
    # 连接信号
    button.button_clicked.connect(listener1.update)
    button.button_clicked.connect(listener2.update)

    button.click()

    # 断开信号示例
    # button.button_clicked.disconnect(listener1.update)