现在很多程序员在面试的时候都遇到过这个问题---<猫叫了,老鼠跑了,主人醒了...>,实现一个连动效果,我也遇到过,感觉这道面试题目挺经典的,挺考验面向对象设计(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();
}
}
}