zoukankan      html  css  js  c++  java
  • 观察者模式-猫叫了,老鼠跑了,主人醒了...

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

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

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

        


     1 namespace NewCatAndMouse
     2 {
     3     public abstract class BaseObject
     4     {
     5         private string name;
     6 
     7         public string Name
     8         {
     9             get { return name; }
    10             set { name = value; }
    11         }
    12 
    13         /// <summary>
    14         /// 抽象出的行为
    15         /// </summary>
    16         public abstract void Action();
    17     }
    18 }

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


     1 namespace NewCatAndMouse
     2 {
     3     public class ActionHandle
     4     {
     5         private BaseObject manager;
     6 
     7         public ActionHandle(BaseObject manager,string name)
     8         {
     9             this.manager = manager;
    10             this.manager.Name = name;
    11         }
    12 
    13         /// <summary>
    14         /// 执行
    15         /// </summary>
    16         public void Execute()
    17         {
    18             this.manager.Action();
    19         }
    20     }
    21 }

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

    复制代码
     1 namespace NewCatAndMouse
     2 {
     3     public class Cat:BaseObject
     4     {
     5         public override void Action()
     6         {
     7             Console.Write(this.Name+"(猫)大吼一声!"+" ");
     8         }
     9     }
    10 }
    复制代码
    复制代码
     1 namespace NewCatAndMouse
     2 {
     3     public class Mouse:BaseObject
     4     {
     5         public override void Action()
     6         {
     7             Console.Write(this.Name+"(老鼠)仓惶逃跑!"+" ");
     8         }
     9     }
    10 }
    复制代码
    复制代码
     1 namespace NewCatAndMouse
     2 {
     3     public class Master:BaseObject
     4     {
     5         public override void Action()
     6         {
     7             Console.Write(this.Name+"(主人)猛然惊醒!" + " ");
     8         }
     9     }
    10 }
    复制代码

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


     1 <?xml version="1.0" encoding="utf-8" ?>
     2 <configuration>
     3   <connectionStrings>
     4     <add name="AssemblyName" connectionString="NewCatAndMouse"/>
     5   </connectionStrings>
     6   <appSettings>
     7     <add key="Cat" value="Tom"/>
     8     <add key="Mouse" value="Jerry"/>
     9     <add key="Master" value="Bob"/>
    10   </appSettings>
    11 </configuration>

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


     1 namespace NewCatAndMouse
     2 {
     3 
     4     public class SubjectAggregate
     5     {
     6         private static List<string> list = new List<string>();
     7         /// <summary>
     8         /// 将配置文件里的所有键读入集合,并返回
     9         /// </summary>
    10         public static List<string> GetAllObject()
    11         {
    12             foreach(string key in ConfigurationManager.AppSettings.AllKeys)
    13             {
    14                 list.Add(key);
    15             }
    16 
    17             if (list.Count < 1)
    18             {
    19                 return null;
    20             }
    21             else
    22             {
    23                 return list;
    24             }
    25         }
    26         
    27     }
    28 }

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


     1 namespace NewCatAndMouse
     2 {
     3     public class ReflectionObject
     4     {
     5 
     6         private static string assemblyName = System.Configuration.ConfigurationManager.ConnectionStrings["AssemblyName"].ConnectionString;
     7         
     8         /// <summary>
     9         /// 通过反射返回指定的类的实例
    10         /// </summary>
    11         /// <param name="key"></param>
    12         /// <returns></returns>
    13         public static BaseObject GetObject(string className)
    14         {
    15             return (BaseObject)Assembly.Load(assemblyName).CreateInstance(assemblyName+"."+className);
    16         }
    17     }
    18 }

           下面就是客户端代码了:


     1 namespace NewCatAndMouse
     2 {
     3     class Program
     4     {
     5         static void Main(string[] args)
     6         {
     8             List<string> list = SubjectAggregate.GetAllObject();
     9             if (list != null)
    10             {
    11                 for (int i = 0; i < list.Count; i++)
    12                 {
    13                     ActionHandle handle = new ActionHandle(ReflectionObject.GetObject(list[i]),System.Configuration.ConfigurationManager.AppSettings[list[i]].ToString());
    14                     handle.Execute();
    15                 }
    16             }
    17             else
    18             {
    19                 Console.Write("Not fount object!");
    20             }
    21 22             Console.Read();
    23         }
    24     }
    25 }

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

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

             可能有人会说:那么简单的代码也好意思发上来。我想说:功能总能实现,就看怎样实现。这只是一个思路,由于更多的人把自己总结的不错的设计思路分享出来,所以我们才能进步。

             最近看设计模式有些上瘾,所以手痒也来凑凑热闹,但毕竟学程序的时间太短,经验太浅,所以可能有很多问题。希望大家帮我指正,希望能和大家一起努力,共同进步。

             附:让板砖来的更激烈些吧!

  • 相关阅读:
    【webpack4x】基本概念
    React平时的一些踩坑总结
    redux-saga学习进阶篇二
    redux-saga学习进阶篇一
    redux-saga学习基础篇一
    性能优化之节流(throttling)与防抖(debounce)
    git操作之发现push到远程仓库的代码有误
    git高级浅入之当我们需要去恢复到某个版本
    git高级浅入之当我们需要修改某次commit信息
    http验证CertificateValidation
  • 原文地址:https://www.cnblogs.com/binyao/p/4898144.html
Copyright © 2011-2022 走看看