概述
代理在实际中应用场景主要为,在对一个已有的类的方法,进行前后记录一些操作。如当一个程序的方法运行时前后,添加一些日志记录或时间记录,以测量该方法运行所耗的时间。代理的实现分静态代理和动态代理,静态代理主要可通过继承与聚合进行实现。其中继承的实现,主要是在对需要添加代理的类进行扩展,然后按各种需求进行扩展,但此种方式的缺点是,如果同时需要两个代理功能,如先记录时间,然后记录日志,则可先让时间记录继承业务类,然后日志记录继承时间记录。但如果还需要先日志后记录时间,则又得再次写一套继承。如此,会导致类无快速增加。聚合的实现,则只需要将所实现同一接口的业务类对象进行先后传递即可。以下一步步在此,仅以简单的例子,一步步实现代理的实现原理。
实现
一、静态代理实现
1.基本的业务。假设有一个接口Runnable,现在让一个Car去实现它
namespace FD.Pattern.ConsoleApplicationProxy { // CreatedOn: 2016-04-12 // CreatedBy: Jackie Lee /// <summary> /// 移动的接口 /// </summary> interface Runnable { void Run(); } } namespace FD.Pattern.ConsoleApplicationProxy { // CreatedOn: 2016-04-12 // CreatedBy: Jackie Lee /// <summary> /// 汽车 /// </summary> public class Car : Runnable { public void Run() { Console.WriteLine("The car is running..."); Thread.Sleep(500); } } }
为了后面能测试运行时间,在Car的实现中休息500毫秒。
2.新增要求。现在要求在不改变Car的情况下,记录下Car的Run运行时间,Car的Run情况信息进行日志记录。以下使用聚合方式实现简单的静态代理
namespace FD.Pattern.ConsoleApplicationProxy { // CreatedOn: 2016-04-12 // CreatedBy: Jackie Lee /// <summary> /// 汽车移动日期代理 /// </summary> public class CarRunDateProxy : Runnable { private Car car; public CarRunDateProxy(Car car) { this.car = car; } public void Run() { Stopwatch watch = Stopwatch.StartNew(); car.Run(); Console.WriteLine("Car Move {0} ms", watch.ElapsedMilliseconds); } } } namespace FD.Pattern.ConsoleApplicationProxy { // CreatedOn: 2016-04-12 // CreatedBy: Jackie Lee /// <summary> /// 汽车移动日志代理 /// </summary> public class CarRunLogProxy : Runnable { private Car car; public CarRunLogProxy(Car car) { this.car = car; } public void Run() { Console.WriteLine("the car is begin to run..."); car.Run(); Console.WriteLine("the car stops running"); } } }
如下测试情况,效果
namespace FD.Pattern.ConsoleApplicationProxy { // CreatedOn: 2016-04-12 // CreatedBy: Jackie Lee /// <summary> /// 主程序 /// </summary> class Program { static void Main(string[] args) { Car c = new Car(); CarRunDateProxy cmdp = new CarRunDateProxy(c); cmdp.Run(); Console.WriteLine("========================="); CarRunLogProxy cmlp = new CarRunLogProxy(c); cmlp.Run(); Console.ReadKey(); } } }
效果:
为了实现可以同时组合多个代理,需要让代理传入的参数为抽象为接口Runnable,以下改进,如CarRunLogProxy将Car类型修改为Runnable接口,如下
1 public class CarRunLogProxy : Runnable 2 { 3 private Runnable run; 4 public CarRunLogProxy(Runnable r) 5 { 6 this.run = r; 7 } 8 9 public void Run() 10 { 11 Console.WriteLine("the car is begin to run..."); 12 run.Run(); 13 Console.WriteLine("the car stops running"); 14 }
同样将CarRunDateProxy中的Car也改为Runnable接口,此时,可以实现任意组合进行记录信息。
先记录运行时间,现记录运行日志,在测试
1 Car c = new Car(); 2 CarRunDateProxy cmdp = new CarRunDateProxy(c); 3 Console.WriteLine("========================="); 4 5 Console.WriteLine("DateProxy, LogProxy"); 6 CarRunLogProxy cmlp = new CarRunLogProxy(cmdp); 7 cmlp.Run();
效果:
先记录日志,然后记录运行时间,则如:
CarRunLogProxy cmlp = new CarRunLogProxy(c); CarRunDateProxy cmdp = new CarRunDateProxy(cmlp); Console.WriteLine("========================="); Console.WriteLine("LogProxy,DateProxy"); cmdp.Run();