現在很多程序員在面試的時候都遇到過這個問題---<貓叫了,老鼠跑了,主人醒了...>,實現一個連動效果,我也遇到過,感覺這道面試題目挺經典的,挺考驗面向對象設計(OOD)的能力,雖然是個很簡單的例子,但要考慮到程序的擴展性。比如說有新的需求,要求後面再加上狗叫了,那些寫的過死且繁瑣的代碼就要來次大地震了;再比如說又變需求了,貓叫了是因爲被跳蚤咬的,那跳蚤就成爲了導火線,就算是用事件和接口寫出的擴展性很強的程序,也會有點蔫了......
這麼一連串的反應是由一個行爲所引起的,或者是貓叫,亦或者是一個按鈕的點擊引發,如何能讓這一連串反映的擴展性更強,能更堅強的面對新的需求。這就需要一個更佳的思路,更佳的設計模式,今天無意想起了這個問題,也根據我的思路寫了一套模式,下面就詳細的說下我的想法:
無論是貓叫,還是老鼠跑,都是一個行爲,我們把這個行爲抽象出一個基類:
namespace NewCatAndMouse
{
public abstract class BaseObject
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
/// <summary>
/// 抽象出的行爲
/// </summary>
public abstract void Action();
}
}
現在我們再建一箇中間層,用來處理這些行爲:
namespace NewCatAndMouse
{
public class ActionHandle
{
private BaseObject manager;
public ActionHandle(BaseObject manager,string name)
{
this.manager = manager;
this.manager.Name = name;
}
/// <summary>
/// 執行
/// </summary>
public void Execute()
{
this.manager.Action();
}
}
}
現在我們一一實現貓、老鼠和主人(從基類繼承):
namespace NewCatAndMouse
{
public class Cat:BaseObject
{
public override void Action()
{
Console.Write(this.Name+"(貓)大吼一聲!"+"/n");
}
}
}
namespace NewCatAndMouse
{
public class Mouse:BaseObject
{
public override void Action()
{
Console.Write(this.Name+"(老鼠)倉惶逃跑!"+"/n");
}
}
}
namespace NewCatAndMouse
{
public class Master:BaseObject
{
public override void Action()
{
Console.Write(this.Name+"(主人)猛然驚醒!" + "/n");
}
}
}
三個實現類完成了。現在一一實例化,組合調用?不,那樣客戶端會顯的臃腫而醜陋,有人說:代碼是門技術,更是門藝術。所以我們的客戶端代碼應當越簡潔越好。所以,我們把需要的東西寫在配置文件裏:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="AssemblyName" connectionString="NewCatAndMouse"/>
</connectionStrings>
<appSettings>
<add key="Cat" value="Tom"/>
<add key="Mouse" value="Jerry"/>
<add key="Master" value="Bob"/>
</appSettings>
</configuration>
然後我們再做一個類來處理配置文件:
namespace NewCatAndMouse
{
public class SubjectAggregate
{
private static List<string> list = new List<string>();
/// <summary>
/// 將配置文件裏的所有鍵讀入集合,並返回
/// </summary>
public static List<string> GetAllObject()
{
foreach(string key in ConfigurationManager.AppSettings.AllKeys)
{
list.Add(key);
}
if (list.Count < 1)
{
return null;
}
else
{
return list;
}
}
}
}
剛纔說爲了客戶端的乾淨整潔,不要把過多的實例化放在客戶端,所以我們就用反射來實例化類:
namespace NewCatAndMouse
{
public class ReflectionObject
{
private static string assemblyName = System.Configuration.ConfigurationManager.ConnectionStrings["AssemblyName"].ConnectionString;
/// <summary>
/// 通過反射返回指定的類的實例
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static BaseObject GetObject(string className)
{
return (BaseObject)Assembly.Load(assemblyName).CreateInstance(assemblyName+"."+className);
}
}
}
下面就是客戶端代碼了:
namespace NewCatAndMouse
{
class Program
{
static void Main(string[] args)
{
List<string> list = SubjectAggregate.GetAllObject();
if (list != null)
{
for (int i = 0; i < list.Count; i++)
{
ActionHandle handle = new ActionHandle(ReflectionObject.GetObject(list[i]),System.Configuration.ConfigurationManager.AppSettings[list[i]].ToString());
handle.Execute();
}
}
else
{
Console.Write("Not fount object!");
}
Console.Read();
}
}
}
這樣就可以了,如果需要新的子類直接繼承基類,再在配置文件添加一個子類屬性就可以了。而且可以再配置文件裏自由的組合而無需改動客戶端的代碼,符合了開放--封閉原則。
但由於用了反射,所以性能會有些差。而且如果實現類需要有新功能,就得在基類添加,如果功能太多基類就會變的臃腫不堪。所以,它也是有侷限性的,最好是派生類不多而且行爲較爲統一。
用事件來實現:
貓類:定義一個貓叫事件;
老鼠類:訂閱貓叫事件,在貓發出叫聲這個事件後,老鼠逃跑;
主人類:類似於老鼠類,在貓發出叫聲這個事件後,主人醒來;
貓類實現如下:
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace CarCry
{
/// <summary>
/// 貓類的定義
/// </summary>
public class Cat
{
//貓名
private string _name;
//貓叫事件
public event EventHandler<CatCryEventArgs> CatCryEvent;
/// <summary>
/// 構造函數
/// </summary>
/// <param name="name">名字參數</param>
public Cat(string name)
{
_name = name;
}
/// <summary>
/// 促發貓叫的事件
/// </summary>
public void CatCry()
{
CatCryEventArgs args = new CatCryEventArgs(_name);
Console.WriteLine(args);
CatCryEvent(this,args);
}
}
/// <summary>
/// 貓叫事件參數
/// </summary>
public class CatCryEventArgs:EventArgs
{
//發出叫聲的貓的名字
private string _catname;
/// <summary>
/// 構造函數
/// </summary>
public CatCryEventArgs(string catname):base()
{
_catname = catname;
}
/// <summary>
/// 輸出參數內容
/// </summary>
public override string ToString()
{
return "貓 "+ _catname + " 叫了";
}
}
}
老鼠類實現如下:
using System.Collections.Generic;
using System.Text;
namespace CarCry
{
public class Mouse
{
//老鼠名字
private string _name;
/// <summary>
/// 構造函數
/// </summary>
/// <param name="name">老鼠的名字</param>
/// <param name="cat">發出叫聲的貓</param>
public Mouse(string name,Cat cat)
{
_name = name;
cat.CatCryEvent += CatCryHandle;//訂閱貓叫事件
}
/// <summary>
/// 貓叫事件處理
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void CatCryHandle(object sender,CatCryEventArgs args)
{
Run();
}
/// <summary>
/// 逃跑方法
/// </summary>
private void Run()
{
Console.WriteLine("老鼠 " + _name + " 逃跑了");
}
}
}
主人類實現如下:
using System.Collections.Generic;
using System.Text;
namespace CarCry
{
public class Master
{
//主人名字
private string _name;
/// <summary>
/// 構造函數,訂閱事件
/// </summary>
/// <param name="name">主人名字</param>
/// <param name="cat">貓</param>
public Master(string name,Cat cat)
{
_name = name;
cat.CatCryEvent += CatCryHandler;//訂閱貓叫事件
}
/// <summary>
/// 貓叫事件處理
/// </summary>
/// <param name="sender"></param>
/// <param name="args">貓叫事件</param>
private void CatCryHandler(object sender,CatCryEventArgs args)
{
WakeUp();
}
/// <summary>
/// 主人醒了事件
/// </summary>
private void WakeUp()
{
Console.WriteLine("主人 "+_name+" 醒了") ;
}
}
}
主函數的調用如下:
using System.Collections.Generic;
using System.Text;
namespace CarCry
{
class MainClass
{
static void Main(string[] args)
{
Console.WriteLine("開始模擬");
Cat cat = new Cat("Tom");
Mouse mouse1 = new Mouse("Jack", cat);
Mouse mouse2 = new Mouse("jackson",cat);
Master master = new Master("Tao", cat);
Master master2 = new Master("Hong",cat);
cat.CatCry();
Console.ReadLine();
}
}
}