1:主题拆解
①基本介绍
②飞猪购买火车票
③代理模式的优缺点
④手写源码模拟适用场景
⑤代理模式与装饰模式的不同
2:基本介绍
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是中介。
代理模式是一种应用很广泛的结构型设计模式,而且变化很多。在代理模式中引入了一个新的代理对象,代理对象可以在客户端对象和目标对象之间起到中介的作用,去掉客户不能看到的内容或者增添客户需要的额外服务。
AOP面向切面编程,代理模式是一个极简的设计模式,也是AOP实践最最核心的。
3:飞猪购买火车票
1:基础版
回想10多年前或者我们的上一辈购买火车票的场景。
①基础接口
public interface ISubject
{
/// <summary>
/// 看看有没有票
/// </summary>
/// <returns></returns>
bool CheckTicket();
/// <summary>
/// 排队买票
/// </summary>
void TakeTicket();
}
②继承接口并且实现购买火车票的方法
public class RealSubject : ISubject
{
public RealSubject()
{
Thread.Sleep(100);
long lResult = 0;
for (int i = 0; i < 10000000; i++)
{
lResult += i;
}
Console.WriteLine("RealSubject被构造。。。。");
}
public bool CheckTicket()
{
Console.WriteLine("坐车去火车站看余票信息");
Thread.Sleep(1000);
Console.WriteLine("到火车站,看到有票");
return true;
}
public void TakeTicket()
{
Console.WriteLine("开始排队");
Thread.Sleep(1000);
Console.WriteLine("终于买到票了");
}
}
③上端调用
ISubject subject = new RealSubject();
subject.CheckTicket();
subject.TakeTicket();
分析:此时已经实现了一个去车站购买火车票的流程。
根据实际场景我们都知道,每次去买票都要进过如下一系列流程:去火车站->查询余票->有余票->开始排队->很长时间等候->终于买到票。
假如我们去火车站发现没有余票,或者排了很长的对结果票卖完了?怎么办?下次又得重新开始。
2:飞猪版
随着社会的进步,我们现在购买火车票不用专门坐车去火车站,小手一点飞猪就能搞定。
①添加代理类
public class ProxySubject : ISubject
{
private ISubject _ISubject = new RealSubject();
public bool CheckTicket()
{
Console.WriteLine("prepare CheckTicket");
return this._ISubject.CheckTicket();
}
public void TakeTicket()
{
Console.WriteLine("prepare TakeTicket");
this._ISubject.TakeTicket();
}
}
②上端调用
ISubject subject = new ProxySubject();
subject.CheckTicket();
subject.TakeTicket();
分析:此时就是添加了中间的代理层,由代理层去执行真实发生的业务,而上端只用与代理进行连接即可。
4:代理模式的优缺点
1:优点
降低耦合度:代理模式能够协调调用者以及被调用者,一定程度上降低了系统的耦合度
灵活可扩展:客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源码,符合OCP,系统具有较好的灵活性和可扩展性。
提高整体效率(远程代理):远程代理为位于两个不同的地址空间对象的访问提供了一种实现机制,可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高系统整体运行效率。
节约开销(虚拟代理):虚拟代理通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销。
控制权限(保护代理):保护代理可以控制一个对象的访问权限,为不同用户提供不同级别的使用权限。
2:缺点
速度变慢:由于在客户端以及真实主题之间增加了代理对象,因此可能会造成处理速度变慢,比如保护代理。
实现复杂:实现代理模式需要额外的操作,有些代理模式其实很复杂,比如远程代理。
5:手写源码模拟各种代理
1:日志代理
见上面的飞猪版,实际上就是在代理中已经实现了日志,直接通过代理来添加日志,保证real“纯洁性”,而且扩展功对修改关闭。这就是AOP面向切面编程的一种思想。
2:异常代理
做好异常处理---try-catch---通过升级proxy,避免修改real
public class ProxySubject : ISubject
{
private ISubject _ISubject = new RealSubject();
public bool CheckTicket()
{
try
{
Console.WriteLine("prepare CheckTicket");
return this._ISubject.CheckTicket();
}
catch (Exception)
{
throw;
}
}
public void TakeTicket()
{
try
{
Console.WriteLine("prepare TakeTicket");
this._ISubject.TakeTicket();
}
catch (Exception)
{
throw;
}
}
}
3:延迟代理
public class ProxySubject : ISubject
{
private static ISubject _ISubject = null;//延迟代理
private void IniSubject()
{
_ISubject = new RealSubject();//延迟代理
}
public bool CheckTicket()
{
if (_ISubject == null)
{
this.IniSubject();//延迟代理,延迟对象的创建
}
bool bResult = _ISubject.CheckTicket();
return bResult;
}
public void TakeTicket()
{
if (_ISubject == null)
{
this.IniSubject();//延迟代理,延迟对象的创建
}
_ISubject.TakeTicket();
}
}
4:权限代理
权限,鉴权-授权-认证,方法不能直接调用,需要验证权限-登陆
加个参数--用户信息--方法里面校验---通过才允许执行---
Web没有加参数,而是放在HttpContext.Current.Session
权限验证逻辑是很麻烦的,多种多样,不能都放在Real
通过升级proxy,避免修改real
public class ProxySubject : ISubject
{
private ISubject _ISubject = new RealSubject();
public bool CheckTicket()
{
object user = CallContext.GetData("CurrentUser");
if (user == null)
{
throw new Exception("没有权限访问");
}
bool bResult = _ISubject.CheckTicket();
return bResult;
}
public void TakeTicket()
{
object user = CallContext.GetData("CurrentUser");
if (user == null)
{
throw new Exception("没有权限访问");
}
_ISubject.TakeTicket();
}
}
5:单例代理
单例就进程中某个对象只有一个实例--就是升级类对象,添加单例逻辑。
在proxy里面完成---通过升级proxy,避免修改real
public class ProxySubject : ISubject
{
private static ISubject _ISubject = new RealSubject();
public bool CheckTicket()
{
return _ISubject.CheckTicket();
}
public void TakeTicket()
{
_ISubject.TakeTicket();
}
}
6:缓存代理
系统性能优化的第一步就是使用缓存
缓存:第一次请求得到的结果找个地方存起来,下次直接用,以节约时间
封装一个第三方缓存---代理查询时,优先缓存---有缓存效果,性能提升
通过升级proxy,避免修改real
public class ProxySubject : ISubject
{
private ISubject _ISubject = new RealSubject();
private static Dictionary<string, bool> _Dictionary = new Dictionary<string, bool>();//缓存代理
public bool CheckTicket()
{
string key = "mm";
bool bResult = false;
if (_Dictionary.ContainsKey(key))
{
bResult = _Dictionary[key];
}
else
{
bResult = _ISubject.CheckTicket();
_Dictionary[key] = bResult;
}
return bResult;
}
public void TakeTicket()
{
_ISubject.TakeTicket();
}
}
6:代理模式与装饰模式的不同
1:增加的职责范围问题域不同
代理模式以及装饰模式都能动态地增加职责,但是代理模式增加的是一些全新的职责,比如权限控制,缓存处理,智能引用,远程访问等,这些职责与原有职责不属于同一个问题域。对于装饰模式,为具体构件类增加一些相关的职责,是原有职责的扩展,这些职责属于同一个问题域。
2:目的不同
代理模式的目的是控制对对象的访问,而装饰模式是为对象动态增加功能。