观察者模式经典例子

    技术2022-05-20  31

    现在很多程序员在面试的时候都遇到过这个问题---<猫叫了,老鼠跑了,主人醒了...>,实现一个连动效果,我也遇到过,感觉这道面试题目挺经典的,挺考验面向对象设计(OOD)的能力,虽然是个很简单的例子,但要考虑到程序的扩展性。比如说有新的需求,要求后面再加上狗叫了,那些写的过死且繁琐的代码就要来次大地震了;再比如说又变需求了,猫叫了是因为被跳蚤咬的,那跳蚤就成为了导火线,就算是用事件和接口写出的扩展性很强的程序,也会有点蔫了......

           这么一连串的反应是由一个行为所引起的,或者是猫叫,亦或者是一个按钮的点击引发,如何能让这一连串反映的扩展性更强,能更坚强的面对新的需求。这就需要一个更佳的思路,更佳的设计模式,今天无意想起了这个问题,也根据我的思路写了一套模式,下面就详细的说下我的想法:

           无论是猫叫,还是老鼠跑,都是一个行为,我们把这个行为抽象出一个基类:

    namespace NewCatAndMouse{     public abstract class BaseObject     {         private string name;

            public string Name         {             get { return name; }             set { name = value; }         }

             /// <summary>         /// 抽象出的行为         /// </summary>        public abstract void Action();     }}

          现在我们再建一个中间层,用来处理这些行为:

    namespace NewCatAndMouse{     public class ActionHandle     {         private BaseObject manager;

             public ActionHandle(BaseObject manager,string name)         {             this.manager = manager;             this.manager.Name = name;         }

             /// <summary>         /// 执行         /// </summary>         public void Execute()         {             this.manager.Action();         }     }}

           现在我们一一实现猫、老鼠和主人(从基类继承):

    namespace NewCatAndMouse{   public class Cat:BaseObject      {        public override void Action()         {            Console.Write(this.Name+"(猫)大吼一声!"+"/n");         }     }}

    namespace NewCatAndMouse{    public class Mouse:BaseObject     {        public override void Action()         {             Console.Write(this.Name+"(老鼠)仓惶逃跑!"+"/n");         }     }}

    namespace NewCatAndMouse{     public class Master:BaseObject     {          public override void Action()         {             Console.Write(this.Name+"(主人)猛然惊醒!" + "/n");         }     }}

    三个实现类完成了。现在一一实例化,组合调用?不,那样客户端会显的臃肿而丑陋,有人说:代码是门技术,更是门艺术。所以我们的客户端代码应当越简洁越好。所以,我们把需要的东西写在配置文件里:

    <?xml version="1.0" encoding="utf-8" ?><configuration>   <connectionStrings>      <add name="AssemblyName" connectionString="NewCatAndMouse"/>   </connectionStrings>   <appSettings>     <add key="Cat" value="Tom"/>     <add key="Mouse" value="Jerry"/>    <add key="Master" value="Bob"/>   </appSettings></configuration>

    然后我们再做一个类来处理配置文件:

    namespace NewCatAndMouse{

         public class SubjectAggregate     {         private static List<string> list = new List<string>();         /// <summary>         /// 将配置文件里的所有键读入集合,并返回         /// </summary>         public static List<string> GetAllObject()         {             foreach(string key in ConfigurationManager.AppSettings.AllKeys)             {                 list.Add(key);             }

                 if (list.Count < 1)             {                 return null;             }             else             {                 return list;             }         }              }}

           刚才说为了客户端的干净整洁,不要把过多的实例化放在客户端,所以我们就用反射来实例化类:

    namespace NewCatAndMouse{     public class ReflectionObject     {

             private static string assemblyName = System.Configuration.ConfigurationManager.ConnectionStrings["AssemblyName"].ConnectionString;                  /// <summary>         /// 通过反射返回指定的类的实例         /// </summary>         /// <param name="key"></param>         /// <returns></returns>         public static BaseObject GetObject(string className)         {             return (BaseObject)Assembly.Load(assemblyName).CreateInstance(assemblyName+"."+className);         }     }}

           下面就是客户端代码了:

    namespace NewCatAndMouse{     class Program    {         static void Main(string[] args)         {             List<string> list = SubjectAggregate.GetAllObject();             if (list != null)             {                 for (int i = 0; i < list.Count; i++)                 {                     ActionHandle handle = new ActionHandle(ReflectionObject.GetObject(list[i]),System.Configuration.ConfigurationManager.AppSettings[list[i]].ToString());                     handle.Execute();                 }             }             else             {                 Console.Write("Not fount object!");             }             Console.Read();         }     }}

    这样就可以了,如果需要新的子类直接继承基类,再在配置文件添加一个子类属性就可以了。而且可以再配置文件里自由的组合而无需改动客户端的代码,符合了开放--封闭原则。

             但由于用了反射,所以性能会有些差。而且如果实现类需要有新功能,就得在基类添加,如果功能太多基类就会变的臃肿不堪。所以,它也是有局限性的,最好是派生类不多而且行为较为统一。

     

    用事件来实现:

      实现方法:分3个类实现,分别为猫类,老鼠类,主人类。

    猫类:定义一个猫叫事件;

    老鼠类:订阅猫叫事件,在猫发出叫声这个事件后,老鼠逃跑;

    主人类:类似于老鼠类,在猫发出叫声这个事件后,主人醒来;

    猫类实现如下:

    using  System; using  System.Collections.Generic; using  System.Text; using  System.Data; namespace  CarCry{     ///   <summary>      ///  猫类的定义     ///   </summary>      public   class  Cat    {         // 猫名          private   string  _name;         // 猫叫事件          public   event  EventHandler < CatCryEventArgs >  CatCryEvent;         ///   <summary>          ///  构造函数         ///   </summary>          ///   <param name="name"> 名字参数 </param>          public  Cat( string  name)        {            _name  =  name;        }         ///   <summary>          ///  促发猫叫的事件         ///   </summary>          public   void  CatCry()        {            CatCryEventArgs args  =   new  CatCryEventArgs(_name);            Console.WriteLine(args);            CatCryEvent( this ,args);        }    }     ///   <summary>      ///   猫叫事件参数     ///   </summary>      public   class  CatCryEventArgs:EventArgs    {         // 发出叫声的猫的名字          private   string  _catname;         ///   <summary>          ///  构造函数         ///   </summary>          public  CatCryEventArgs( string  catname): base ()        {            _catname  =  catname;        }         ///   <summary>          ///   输出参数内容         ///   </summary>          public   override   string  ToString()        {             return   " 猫  " +  _catname  +   "  叫了 " ;        }    }}

     

    老鼠类实现如下:

     

    using  System; using  System.Collections.Generic; using  System.Text; namespace  CarCry{     public   class  Mouse    {         // 老鼠名字          private   string  _name;         ///   <summary>          ///  构造函数         ///   </summary>          ///   <param name="name"> 老鼠的名字 </param>          ///   <param name="cat"> 发出叫声的猫 </param>          public  Mouse( string  name,Cat cat)        {            _name  =  name;            cat.CatCryEvent  +=  CatCryHandle; // 订阅猫叫事件         }         ///   <summary>          ///  猫叫事件处理         ///   </summary>          ///   <param name="sender"></param>          ///   <param name="args"></param>          private   void  CatCryHandle( object  sender,CatCryEventArgs args)        {            Run();        }         ///   <summary>          ///  逃跑方法         ///   </summary>          private   void  Run()        {            Console.WriteLine( " 老鼠  "   +  _name  +   "  逃跑了 " );        }    }}

     

    主人类实现如下:

     

    using  System; using  System.Collections.Generic; using  System.Text; namespace  CarCry{     public   class  Master    {         // 主人名字          private   string  _name;         ///   <summary>          ///  构造函数,订阅事件         ///   </summary>          ///   <param name="name"> 主人名字 </param>          ///   <param name="cat"> </param>          public  Master( string  name,Cat cat)        {            _name  =  name;            cat.CatCryEvent  +=  CatCryHandler; // 订阅猫叫事件         }         ///   <summary>          ///  猫叫事件处理         ///   </summary>          ///   <param name="sender"></param>          ///   <param name="args"> 猫叫事件 </param>          private   void  CatCryHandler( object  sender,CatCryEventArgs args)        {            WakeUp();        }         ///   <summary>          ///  主人醒了事件         ///   </summary>          private   void  WakeUp()        {            Console.WriteLine( " 主人  " + _name + "  醒了 " ) ;        }    }}

     

    主函数的调用如下:

     

    using  System; using  System.Collections.Generic; using  System.Text; namespace  CarCry{     class  MainClass    {         static   void  Main( string [] args)        {            Console.WriteLine( " 开始模拟 " );            Cat cat  =   new  Cat( " Tom " );            Mouse mouse1  =   new  Mouse( " Jack " , cat);            Mouse mouse2  =   new  Mouse( " jackson " ,cat);            Master master  =   new  Master( " Tao " , cat);            Master master2  =   new  Master( " Hong " ,cat);            cat.CatCry();            Console.ReadLine();        }    }}

     


    最新回复(0)