面試題:貓叫、老鼠跑、人醒的一點看法


這些天一直在面試中,做着不同的面試題,唉,悲催

上周做的一道面試題今天正好出現在園里的首頁,看了一下這位同學的實現,基本上方向是對的,就是代碼上細節沒有注意,有一些錯誤,這里我就寫一下我的理解

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

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)尤其是調用代碼部分,感覺很別扭

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM