罗曼蒂克是奢侈滴

生命不息,学习不止

导航

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

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

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

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

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)尤其是调用代码部分,感觉很别扭

 

 

 

 

 

 

posted on 2012-11-13 10:54  simfe  阅读(4307)  评论(20编辑  收藏  举报