• 面试题:猫叫、老鼠跑、人醒的一点看法


    这些天一直在面试中,做着不同的面试题,唉,悲催

    上周做的一道面试题今天正好出现在园里的首页,看了一下这位同学的实现,基本上方向是对的,就是代码上细节没有注意,有一些错误,这里我就写一下我的理解

    ---------------------------

    C#面试题:

    猫大叫一声,所有的老鼠都开始逃跑,主人被惊醒。

    要求:

    1、要有联动性,老鼠和人的行为是被动的

    2、考虑可扩展行,猫叫声可能会引起其他联动效应

     -------------

    步骤1:先提取出对象,猫、老鼠、人(Cat,Mouse,Person)

    步骤2:再提取对象的动作,猫叫、老鼠跑、人醒(Cat.Call,Mouse.Run,Person.Wake)

    步骤3:关联关系,

    这里有两种理解

    1)  猫叫->老鼠跑

    猫叫->人醒

    2)  猫叫->老鼠跑->人醒(多级联动)

    步骤4:很明显,对应c#这里可以直接使用委托链和事件实现

    另外可以使用设计模式中的观察者模式

    ------------------------------------------

    以下分别给出对应代码:

    观察者模式:这里的老鼠有两种实现方式,对应步骤3中的关联关系

    声明接口

    1 //观察者
    2 public interface IObserver
    3 {
    4      void Update(ISubject sub);
    5 }
    1 //被观察者
    2 public interface ISubject
    3 {
    4      void Notify();
    5      void AddObserver(IObserver o);
    6      void RemoveObserver(IObserver o);
    7 }
     1 //
     2 public class Cat:ISubject
     3 {
     4     //缓存自己的观察者
     5     private System.Collections.Generic.List<IObserver> _list;
     6     public Cat()
     7     {
     8         _list=new System.Collections.Generic.List<IObserver>();
     9         
    10     }
    11     //通知所有自己得观察者
    12     public void Notify()
    13     {
    14         foreach(IObserver o in  _list)
    15         {
    16             o.Update(this);
    17         }
    18     }
    19     public void AddObserver(IObserver o)
    20     {
    21         _list.Add(o);
    22     }
    23     public void RemoveObserver(IObserver o)
    24     {
    25         _list.Remove(o);
    26     }
    27     //猫叫的时候通知观察者
    28     public void Call()
    29     {
    30         System.Console.WriteLine("Cat Call");
    31         Notify();
    32     }
    33 }
     1 //这里对应步骤3中关联关系的第一种理解
     2 //猫叫->老鼠跑
     3 //猫叫->人醒
     4 
     5 public class Mouse:IObserver
     6 {
     7     public void Update(ISubject sub) 
     8     {
     9         Run();
    10     }
    11     public void Run()
    12     {
    13         System.Console.WriteLine("Mouse Run");
    14     }
    15 }
     1 //这里对应步骤3中关联关系的第二种理解
     2 //猫叫->老鼠跑->人醒
     3 //这里老鼠是猫的观察者,同时被人观察,所以两个接口都实现
     4 public class MouseA:IObserver,ISubject
     5 {
     6     //实现观察者接口
     7     public void Update(ISubject sub) 
     8     {
     9         //注意,这里当猫通知老鼠后,老鼠要接着改变自己得状态,并通知自己得观察者
    10         Run();    
    11         
    12     }
    13     public void Run()
    14     {
    15         System.Console.WriteLine("MouseA Run");
    16         //这里老鼠的跑动通知观察者
    17         Notify();
    18     }
    19     
    20     #region//实现被观察者接口
    21     private System.Collections.Generic.List<IObserver> _list;
    22     public MouseA()
    23     {
    24         _list=new System.Collections.Generic.List<IObserver>();        
    25     }
    26     
    27     public void Notify()
    28     {
    29         foreach(IObserver o in  _list)
    30         {
    31             o.Update(this);
    32         }
    33     }
    34     public void AddObserver(IObserver o)
    35     {
    36         _list.Add(o);
    37     }
    38     public void RemoveObserver(IObserver o)
    39     {
    40         _list.Remove(o);
    41     }
    42     #endregion 
    43 }
    //
    public class Person:IObserver{
    
        public void Update(ISubject sub)
        {
            Wake();
        }
        public void Wake()
        {
            System.Console.WriteLine("People Wake");
        }
    }

    调用方法:

    对应第一种:猫同时有两个观察者,或更多个,只需要调用AddObserver加入就行了

     1     public static void Main()
     2     {
     3         //被观察者
     4         ISubject c=new Cat();
     5         //观察者
     6         IObserver m=new Mouse();
     7         IObserver p=new Person();
     8         //加入观察者
     9         c.AddObserver(m);
    10         c.AddObserver(p);
    11         //被观察者状态改变
    12         ((Cat)c).Call();
    13     }

    对应第二种,猫有老鼠一个观察者,老鼠也有人一个观察者,多级联动关系

     1 public static void Main()
     2     {
     3         //被观察者
     4         ISubject c=new Cat();
     5         //既是观察者,也是被观察者
     6         IObserver m=new MouseA();    
     7         //加入观察者
     8         c.AddObserver(m);    
     9         
    10         //观察者
    11         IObserver p=new Person();
    12         //加入观察者    
    13         ((ISubject)m).AddObserver(p);
    14         //被观察者状态改变
    15         ((Cat)c).Call();
    16     }

    设计模式的使用最大的好处是,解耦合,

    这位同学的观察者模式用的是没有错误的,但是代码中有问题,主要是Mouse和Peolple的构造函数中的参数,直接使用了Cat这个对象,这样就使得Mouse、Peolple与Cat绑死在一起,这里的Cat应该换成接口Subject

     -----------------------------以下使用事件和委托实现相同的功能,分为三种实现,第一种是单纯的类,并未解耦合,第二种使用接口解耦合,第三种使用抽象类

     1 public delegate void NotifyEventHandler(object sender,EventArgs e);
     2 
     3 public class TCat
     4 {
     5     public event NotifyEventHandler OnNotify;
     6     public void DoOnNotify(EventArgs e)
     7     {
     8         if (OnNotify!=null)
     9             OnNotify(this,e);
    10     }
    11     
    12     public void Call()
    13     {
    14         System.Console.WriteLine("TCat Call");
    15         EventArgs e=new EventArgs();
    16         DoOnNotify(e);
    17     }
    18 }
    19 
    20 public class TMouse
    21 {
    22     private TCat _Cat;
    23     public TMouse(TCat c)
    24     {
    25         _Cat=c;
    26         c.OnNotify+=new NotifyEventHandler(OnMouseRun);
    27     }
    28     void OnMouseRun(object sender,EventArgs e)
    29     {        
    30         Run();
    31     }
    32     
    33     public void Run()
    34     {
    35         System.Console.WriteLine("TMouse Run");
    36     }
    37 }
    38 
    39 public class TPeople
    40 {
    41     private TCat _Cat;
    42     public TPeople(TCat c)
    43     {
    44         _Cat=c;
    45         c.OnNotify+=new NotifyEventHandler(OnPeopleWake);
    46     }
    47     void OnPeopleWake(object sender,EventArgs e)
    48     {
    49         Wake();
    50     }    
    51     public void Wake()
    52     {
    53         System.Console.WriteLine("TPeople Wake");
    54     }
    55 }

    使用方法:

    1 public static void Main3()
    2 {
    3     TCat c=new TCat();        
    4     TMouse m=new TMouse(c);
    5     TPeople p=new TPeople(c);
    6     c.Call();
    7 }

    事件的第二种实现:

     1 public interface INofify
     2 {
     3     event NotifyEventHandler OnNotify;
     4 }
     5 
     6 public delegate void NotifyEventHandler(object sender,EventArgs e);
     7 
     8 public class CCat:INofify
     9 {
    10     public event NotifyEventHandler OnNotify;
    11     public void DoOnNotify(EventArgs e)
    12     {
    13         if (OnNotify!=null)
    14             OnNotify(this,e);
    15     }
    16     
    17     public void Call()
    18     {
    19         System.Console.WriteLine("CCat Call");
    20         EventArgs e=new EventArgs();
    21         DoOnNotify(e);
    22     }
    23 }
    24 
    25 public class CMouse:INofify
    26 {
    27     private INofify _Cat;
    28     public CMouse(INofify c)
    29     {
    30         _Cat=c;
    31         c.OnNotify+=new NotifyEventHandler(OnMouseRun);
    32     }
    33     void OnMouseRun(object sender,EventArgs e)
    34     {        
    35         Run();
    36     }
    37     
    38     public void Run()
    39     {
    40         System.Console.WriteLine("CMouse Run");
    41         EventArgs e=new EventArgs();
    42         DoOnNotify(e);
    43     }
    44     
    45     public event NotifyEventHandler OnNotify;
    46     public void DoOnNotify(EventArgs e)
    47     {
    48         if (OnNotify!=null)
    49             OnNotify(this,e);
    50     }    
    51 }
    52 
    53 public class CPeople
    54 {
    55     private INofify _Cat;
    56     public CPeople(INofify c)
    57     {
    58         _Cat=c;
    59         c.OnNotify+=new NotifyEventHandler(OnPeopleWake);
    60     }
    61     void OnPeopleWake(object sender,EventArgs e)
    62     {
    63         Wake();
    64     }    
    65     public void Wake()
    66     {
    67         System.Console.WriteLine("CPeople Wake");
    68     }
    69 }

    使用方法:

    1 public static void Main()
    2 {
    3     INofify c=new CCat();        
    4     INofify m=new CMouse(c);
    5     CPeople p=new CPeople(m);//注意,这里的Peolple并未实现INotify接口,如果需要实现步骤3中的更多级级联,请实现这个接口
    6     ((CCat)c).Call();
    7 }

     事件的第三种实现:

     1 ////Notify
     2 public abstract class Notify
     3 {
     4     public event NotifyEventHandler OnNotify;
     5     public void DoOnNotify(EventArgs e)
     6     {
     7         if (OnNotify!=null)
     8             OnNotify(this,e);
     9     }
    10     //这里是缓存被观察者
    11     protected  Notify _subject;
    12     
    13     public Notify(Notify c)
    14     {
    15         if (c!=null)
    16         {
    17             _subject=c;
    18             _subject.OnNotify+=new NotifyEventHandler(Do);
    19         }
    20     }
    21     public abstract void Do(object sender,EventArgs e);
    22     
    23 }
    24 
    25 public class ACat:Notify
    26 {//注意,这里由于继承于抽象类,所以必须要由一个Do函数的实现,函数体为空就可以了,要么就把抽象类的抽象函数改成virtual的
    27     public ACat(Notify c):base(c)
    28     {
    29     }
    30     public void Call()
    31     {
    32         System.Console.WriteLine("ACat Call");
    33         EventArgs e=new EventArgs();
    34         DoOnNotify(e);
    35     }
    36 }
    37 
    38 public class AMouse:Notify
    39 {
    40     public AMouse(Notify c):base(c)
    41     {
    42     }
    43     public override void Do(object sender,EventArgs e)
    44     {
    45         Run();
    46     }
    47     public void Run()
    48     {
    49         System.Console.WriteLine("AMouse Run");
    50         EventArgs e=new EventArgs();
    51         DoOnNotify(e);
    52     }
    53 }
    54 
    55 public class APeople:Notify
    56 {
    57     public APeople(Notify c):base(c)
    58     {
    59     }
    60     public override void Do(object sender,EventArgs e)
    61     {
    62         Wake();
    63     }
    64     public void Wake()
    65     {
    66         System.Console.WriteLine("APeople Wake");
    67     }    
    68     
    69 }

    使用方法:

     1     public static void Main()
     2     {
     3         //不需要被观察者
     4         Notify cat=new ACat(null);    
     5         //观察猫
     6         Notify mouse=new AMouse(cat);
     7         //观察老鼠
     8         Notify people=new APeople(mouse);
     9         ((ACat)cat).Call();
    10     }

    以上各种代码,区别在于使用模式和事件,或者是接口和抽象类,或者是扩展性的区别,另外使用的时候都是非常简单的,几行代码而已。

    还有这位同学的实现,从结果上是正确的,但是缺点也很明显

    1)各个类之间毫无结构性的关联,没有充分利用面向对象的一些特性,扩展性很差

    2)尤其是调用代码部分,感觉很别扭

  • 相关阅读:
    Python关键字
    tomcat中 server.xml
    Java web.xml笔记
    HTML标签笔记
    jsp笔记
    Ubuntu 安装 chrome
    隐藏文件管理器左侧导航栏的部分内容
    virtualbox中设置u盘启动
    剑指Offer题解索引
    当你在浏览器地址栏输入一个URL后回车,将会发生的事情?
  • 原文地址:https://www.cnblogs.com/simfe/p/2767610.html
Copyright © 2020-2023  润新知