C# 发布-订阅模式

前言

发布-订阅者模式,可以使代码解耦、易于扩展。这个模式要用委托和事件。

什么时候应该使用 发布-订阅者模式 呢?场景如下:

草原上有 A、B 两只动物,有一只饥肠辘辘的狮子来到了草原上。如果狮子发现了 A,那 A 就要赶紧跑,B 可以继续猫着;如果狮子发现了 B,那 B 就要跑路,A 可以猫着;如果狮子把 A、B 都看到了,那 A、B 都得跑,谁知道狮子中意追哪个呢;如果狮子谁都没看见,那 A、B 都猫着。

你会发现这个简单的例子用个 if…else 语句就搞定了:

if(狮子发现了A)
{
    A跑路;
}
else if(狮子发现了B)
{
    B跑路;
}
else if(狮子发现了A、B)
{
    A、B跑路;
}
else(狮子谁都没看见)
{
    A、B猫着;
}

但是,我们再想想,如果还有其他动物,那情况是不是就复杂了,比如狮子可能发现了C,也可能发现了A、C,每多一个动物那这段代码就要重新讨论狮子的眼光,效率低,扩展性可以说是 0;

使用发布-订阅者模式可以很好的解决这个问题。

我们定义三个类,分别表示 狮子、A、B。

public class 狮子
{

}
public class A
{

}
public class B
{

}

狮子的类中有 “发现动物” 这个方法,此方法带有一个形参,用于传递发现了哪些动物。

public class 狮子
{
    public void Find(string animals)
    {
       Console.WriteLine("狮子发现了{0}",animals);
    }
}

A、B 类中各有一个跑路方法。

public class A
{
    public void Run()
    {
       Console.WriteLine("我是 A,被狮子发现了,我要跑了");
    }
}
public class B
{
    public void Run()
    {
       Console.WriteLine("我是 B,被狮子发现了,我要跑了");
    }
}

好,现在三个独立的类就构造完了,那怎么让 A、B 获取狮子的状态呢?

我们在命名空间下、三个类的外面,也就是公共代码部分,声明一个带形参的委托 FindEventHandler,命名规则是在准备传递的方法名后加上 EventHandler,这是标准命名法,便于代码阅读。

public delegate void FindEventHandler(string animals);

然后在狮子类中,定义一个事件,并且调用这个事件:

// 基于上面的委托定义事件
public event FindEventHandler FindEvent;
public void Find(string animals)
{
   Console.WriteLine("狮子发现了{0}",animals);
   if(FindEvent != null)
   {
      FindEvent(animals);
   }
}

做完上面两个步骤,只要 A、B 订阅了该事件,就能自动执行,就知道狮子到底发现了谁。订阅方法如下,B与之类似:

public class A
{
   public A(狮子 小狮子)
   {
      小狮子.FindEvent += new FindEventHandler(a_FindEvent);//订阅发现事件
   }

   void a_FindEvent(string animals)
   {
      if(animals.Contains("A"))
      {
        Run();
      }
   }

   public void Run() 
   {
       Console.WriteLine("我是 A,被狮子发现了,我要跑了"); 
   }
}

完整代码如下:

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

namespace 发布_订阅者模式
{
    class Program
    {
       static void Main(string[] args)
       {
          狮子 狮子 = new 狮子();
          A a = new A(狮子);
          B b = new B(狮子);

          狮子.Find("A");

          狮子.Find("B");

          狮子.Find("AB");

          Console.ReadLine();

        // 由于A和B订阅了狮子的事件,所以无需任何代码,A和B均会按照约定进行动作。
      }
    }

    public delegate void FindEventHandler(string animals);

    public class 狮子
    {
         public event FindEventHandler FindEvent;

         public void Find(string animals)
         {
             Console.WriteLine("狮子发现了{0}", animals);
             if (FindEvent != null)
             {
                 FindEvent(animals);
             }
         }
    }

    public class A
    {
        public A(狮子 狮子)
        {
            狮子.FindEvent += new FindEventHandler(a_FindEvent);//订阅发现事件
        }

        void a_FindEvent(string animals)
        {
            if(animals.Contains("A"))
            {
                Run();
            }
        }

        public void Run()
        {
             Console.WriteLine("我是 A,被狮子发现了,我要跑了");
        }
    }

    public class B
    {
         public B(狮子 狮子)
         {
              狮子.FindEvent += new FindEventHandler(b_FindEvent);//订阅发现事件
         }

         void b_FindEvent(string animals)
         {
              if (animals.Contains("B"))
              {
                  Run();
              }
         }

         public void Run()
         {
              Console.WriteLine("我是 B,被狮子发现了,我要跑了");
         }
    }
}

20180320162008

 

上面这个例子是我根据以前看到的例子编的,理解了这个例子,也就理解了委托和事件。关于委托和事件,我在另一篇文章里再细说,传送门:C# 委托和事件

发表评论

Powered by WordPress | Theme Revised from Doo

苏ICP备18047621号

Copyright © 2017-2024 追光者博客