這些天一直在面試中,做着不同的面試題,唉,悲催
上周做的一道面試題今天正好出現在園里的首頁,看了一下這位同學的實現,基本上方向是對的,就是代碼上細節沒有注意,有一些錯誤,這里我就寫一下我的理解
---------------------------
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)尤其是調用代碼部分,感覺很別扭
