C# 观察者模式

一、概述

观察者模式是一种常用的设计模式,它属于行为型模式。在C#中,观察者模式通过定义一种一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。这种模式可以实现松耦合,使得被观察者和观察者之间的关系更加灵活。

在C#中实现观察者模式通常需要以下几个角色:

1. Subject(主题):被观察者,它维护了一个观察者列表,并提供了添加、删除和通知观察者的方法。

2. Observer(观察者):观察者,它定义了一个更新方法,用于接收被观察者发出的通知。

3. ConcreteSubject(具体主题):具体的被观察者,它继承或实现了主题接口,并实现了具体的业务逻辑。它会在自身状态发生变化时通知观察者。

4. ConcreteObserver(具体观察者):具体的观察者,它继承或实现了观察者接口,并实现了更新方法。当接收到被观察者的通知时,它会执行相应的逻辑。

通过使用观察者模式,我们可以实现对象之间的解耦,使得它们之间的依赖关系更加灵活和可扩展。这种模式在事件处理、GUI开发以及许多其他场景中都有广泛应用。

观察者模式的优点和缺点:

观察者模式的优点:

1. 松耦合:被观察者和观察者之间的关系是松耦合的,它们可以独立变化而互不影响。

2. 可扩展性:可以方便地增加新的观察者,或者在不影响现有代码的情况下增加新的被观察者。

3. 易于维护:观察者模式将业务逻辑分散到各个观察者中,使得代码更加清晰、易于维护。

4. 支持广播通信:被观察者可以同时通知多个观察者,实现广播式的通信。

观察者模式的缺点:

1. 观察者过多时的性能问题:如果观察者过多或者观察者的更新操作比较耗时,可能会影响系统的性能。

2. 循环依赖问题:观察者和被观察者之间存在循环依赖的情况下,可能导致系统出现问题。

3. 更新顺序不确定:观察者模式中,观察者的更新顺序是不确定的,可能会导致一些意外的结果。

观察者模式适用于以下场景:

1. 当一个对象的状态变化需要通知其他多个对象,并且这些对象的行为需要根据该状态变化做出相应的调整时,可以使用观察者模式。

2. 当一个对象需要在不知道有多少个其他对象关注它的情况下,动态地将消息通知给这些对象时,可以使用观察者模式。

3. 当一个对象的改变需要同时影响其他多个对象,并且它不希望与这些对象形成紧耦合关系时,可以使用观察者模式。

4. 当系统中的某个对象需要与其他多个对象进行解耦,以降低对象之间的依赖性时,可以使用观察者模式。

5. 当需要实现事件驱动的系统或者消息通知机制时,可以使用观察者模式。

观察者模式适用于多个对象之间存在一对多的依赖关系,当一个对象的状态发生变化时,需要通知其他多个对象进行相应的处理。它能够实现对象之间的解耦,提高系统的灵活性和可扩展性。

二、代码实现

新建一个控制台项目

代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 观察者模式
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ConcreteSubject subject = new ConcreteSubject();
            ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
            ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);
            ConcreteObserver observer3 = new ConcreteObserver("Observer 3", subject);

            subject.AddObserver(observer1);
            subject.AddObserver(observer2);
            subject.AddObserver(observer3);

            subject.State = 1; // 触发通知
            subject.RemoveObserver(observer2);
            subject.State = 2; // 触发通知

            Console.ReadKey();
        }
    }

    // 主题接口
    public interface ISubject
    {
        void AddObserver(IObserver observer);
        void RemoveObserver(IObserver observer);
        void NotifyObservers();
    }

    // 具体主题
    public class ConcreteSubject : ISubject
    {
        private List<IObserver> observers = new List<IObserver>();
        private int state;

        public int State
        {
            get { return state; }
            set
            {
                state = value;
                NotifyObservers();
            }
        }

        public void AddObserver(IObserver observer)
        {
            observers.Add(observer);
        }

        public void RemoveObserver(IObserver observer)
        {
            observers.Remove(observer);
        }

        public void NotifyObservers()
        {
            foreach (IObserver observer in observers)
            {
                observer.Update();
            }
        }
    }

    // 观察者接口
    public interface IObserver
    {
        void Update();
    }

    // 具体观察者
    public class ConcreteObserver : IObserver
    {
        private string name;
        private ConcreteSubject subject;

        public ConcreteObserver(string name, ConcreteSubject subject)
        {
            this.name = name;
            this.subject = subject;
        }

        public void Update()
        {
            Console.WriteLine($"Observer {name} received an update. New state: {subject.State}");
        }
    }
}

运行:

三、解析代码

上面的代码看起来比较复杂,在23个设计模式中,其实还不算最复杂的,看多了习惯就好,下面大致的讲下代码逻辑。

首先是定义了一个接口 ISubject,它有三个方法,添加观察者,移除观察者,通告观察者。

然后 ConcreteSubject 继承了这个接口,这里重点在 state 这个属性这里:

public int State
{
    get { return state; }
    set
    {
        state = value;
        NotifyObservers();
    }
}

如果设置属性的值,就会调用 NotifyObservers 方法,这是个通知所有观察者的一个方法。

ConcreteObserver 存储了 name 和 具体观察者 ConcreteSubject 的实例(具体的主题),不过这里,只是用到了打印 subject.State ,并无其他作用。

Console.WriteLine($"Observer {name} received an update. New state: {subject.State}");

在 ConcreteSubject.AddObserver 方法的参数是一个 IObserver 接口,其实传递的就是 ConcreteObserver 实例,这里由于只需要调用 Update 方法,所以只用到了一个接口 IObserver,虽然保护了 ConcreteObserver 类的开放权限,但使的整个过程看起来更加复杂了。

从工作的角度来说,上面的很多写法并不是那么推荐,将简单的事情复杂化,是在自己在刁难自己,还可能会导致更多的 bug,可别忘了公司里还有项目经理,老板,他们可是一直在催你快点做,他们可不管你代码写的怎么样,他们根本就不懂代码。

在 Main 函数中,实例化了三个 ConcreteObserver 类,并传入了 name , 并添加到了 subject 中,这就是添加了三个观察者,以便后面用消息来通知他们

在设置 subject.State = 1 时,默认就调用了 NotifyObservers 方法,也就调用了三个观察者的 Update 方法,理解了,就会发现这些代码其实没有那么难。

四、案例

下面的代码来源博客 JiYF大男孩,写的很不错,拿来做个参考,链接在下面

https://www.cnblogs.com/JiYF/p/6896458.html

新建类 Blog

using System.Collections.Generic;

/// <summary>  
/// 订阅者接口  
/// </summary>  
public interface IObserver
{
    void Receive(Blog blog);
}

/// <summary>  
/// 订阅博客抽象类  
/// </summary>  
public abstract class Blog
{
    /// <summary>  
    /// 保存订阅者列表  
    /// </summary>  
    private List<IObserver> observers = new List<IObserver>();

    /// <summary>  
    /// 博主名  
    /// </summary>  
    public string BlogName { get; set; }

    /// <summary>  
    /// 博客标题  
    /// </summary>  
    public string BlogTitle { get; set; }

    /// <summary>  
    /// 博客信息  
    /// </summary>  
    public string BlogInfo { get; set; }

    /// <summary>  
    /// 博客构造函数  
    /// </summary>  
    /// <param name="blogTitle">博客标题</param>  
    /// <param name="blogInfo">博客信息</param>  
    public Blog(string name, string blogTitle, string blogInfo)
    {
        this.BlogName = name;
        this.BlogTitle = blogTitle;
        this.BlogInfo = blogInfo;
    }

    /// <summary>  
    /// 添加一个订阅者  
    /// </summary>  
    /// <param name="observer">具体的订阅者对象</param>  
    public void AddObserver(IObserver observer)
    {
        if (observers.Contains(observer))
        {
            return;
        }
        observers.Add(observer);
    }

    /// <summary>  
    /// 删除一个订阅者  
    /// </summary>  
    /// <param name="observer">具体的订阅者对象</param>  
    public void RemoveObserver(IObserver observer)
    {
        if (observers.Contains(observer))
        {
            observers.Remove(observer);
        }
    }

    /// <summary>  
    /// 发布博客通知  
    /// </summary>  
    public void PublishBlog()
    {
        //遍历通知每一个订阅者  
        foreach (IObserver ob in observers)
        {
            if (ob != null)
            {
                // 调用继承当前接口的Receive方法  
                ob.Receive(this);
            }
        }
    }
}  
 

新建类 JiYFBlog

namespace 设计模式_观察者模式
{
    /// <summary>  
    /// 具体的订阅博客类  
    /// </summary>  
    public class JiYFBlog : Blog
    {
        public JiYFBlog(string name, string blogTitile, string blogInfo)
            : base(name, blogTitile, blogInfo)
        {

        }
    }
}

新建类 Observer

using System;

namespace 设计模式_观察者模式
{
    /// <summary>  
    /// 具体的订阅者类  
    /// </summary>  
    public class Observer : IObserver
    {
        /// <summary>  
        /// 订阅者名字  
        /// </summary>  
        private string m_Name;
        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        /// <summary>  
        /// 订阅者构造函数  
        /// </summary>  
        /// <param name="name">订阅者名字</param>  
        public Observer(string name)
        {
            this.m_Name = name;
        }

        /// <summary>  
        /// 订阅者接受函数  
        /// </summary>  
        /// <param name="blog"></param>  
        public void Receive(Blog blog)
        {
            Console.WriteLine("订阅者:\"{0}\"观察到了:{1}发布的一篇博客,标题为:{2},内容为:{3}", Name, blog.BlogName, blog.BlogTitle, blog.BlogInfo);
        }
    }
}

调用方法

using System;

namespace 设计模式_观察者模式
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("--全部订阅者--");
            // 创建一个 JiYF 的博客  
            // 多态的方式发布一条播客,但此时还没有订阅者  
            Blog jiyfBlog = new JiYFBlog("JiYF笨小孩", "丑小鸭", "丑小鸭的故事");

            // 创建订阅者  
            Observer obsZhangsan = new Observer("张三");
            Observer obsLiSi = new Observer("李四");
            Observer obsWangwu = new Observer("王五");

            // 添加到 JiYF 博客的订阅者  
            jiyfBlog.AddObserver(obsZhangsan);
            jiyfBlog.AddObserver(obsLiSi);
            jiyfBlog.AddObserver(obsWangwu);

            //通知订阅者  
            jiyfBlog.PublishBlog();

            Console.WriteLine();
            Console.WriteLine("--移除订阅者张三--");
            jiyfBlog.RemoveObserver(obsZhangsan);
            jiyfBlog.PublishBlog();


            Console.ReadLine();
        }
    }
}

运行:

结束

如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言

end

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
下面是一个 C#观察者模式实例: ```csharp using System; using System.Collections.Generic; // 被观察者接口 interface ISubject { void Attach(IObserver observer); // 添加观察者 void Detach(IObserver observer); // 移除观察者 void Notify(); // 通知观察者 } // 观察者接口 interface IObserver { void Update(ISubject subject); // 接收通知并更新 } // 具体的被观察者类 class ConcreteSubject : ISubject { private List<IObserver> observers = new List<IObserver>(); private string state; // 被观察者状态 // 添加观察者 public void Attach(IObserver observer) { observers.Add(observer); } // 移除观察者 public void Detach(IObserver observer) { observers.Remove(observer); } // 通知观察者 public void Notify() { foreach (IObserver observer in observers) { observer.Update(this); } } // 修改状态并通知观察者 public void SetState(string state) { this.state = state; Notify(); } public string GetState() { return state; } } // 具体的观察者类 class ConcreteObserver : IObserver { private string name; // 观察者名称 public ConcreteObserver(string name) { this.name = name; } // 接收通知并更新 public void Update(ISubject subject) { Console.WriteLine("{0} 接收到了被观察者的通知,当前状态为 {1}", name, ((ConcreteSubject)subject).GetState()); } } // 测试代码 class Program { static void Main(string[] args) { ConcreteSubject subject = new ConcreteSubject(); // 添加观察者 subject.Attach(new ConcreteObserver("观察者 A")); subject.Attach(new ConcreteObserver("观察者 B")); subject.Attach(new ConcreteObserver("观察者 C")); // 修改状态并通知观察者 subject.SetState("状态 1"); // 移除观察者 subject.Detach(new ConcreteObserver("观察者 B")); // 修改状态并通知观察者 subject.SetState("状态 2"); Console.ReadKey(); } } ``` 以上代码实现了一个简单的观察者模式实例,其中 `ConcreteSubject` 是具体的被观察者类,实现了 `ISubject` 接口;`ConcreteObserver` 是具体的观察者类,实现了 `IObserver` 接口。在测试代码中,我们创建了一个 `ConcreteSubject` 对象,并添加了三个观察者,然后修改了状态并通知观察者,最后移除了一个观察者并再次修改状态并通知观察者。运行该程序,控制台输出如下: ``` 观察者 A 接收到了被观察者的通知,当前状态为 状态 1 观察者 B 接收到了被观察者的通知,当前状态为 状态 1 观察者 C 接收到了被观察者的通知,当前状态为 状态 1 观察者 A 接收到了被观察者的通知,当前状态为 状态 2 观察者 C 接收到了被观察者的通知,当前状态为 状态 2 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

熊思宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值