知识大全 C# Design Patterns (4) - Proxy

Posted

篇首语:虚心使人进步,骄傲使人落后,我们应当永远记住这个真理。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 C# Design Patterns (4) - Proxy相关的知识,希望对你有一定的参考价值。

C# Design Patterns (4) - Proxy  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  Proxy Pattern (代理模式)

  The Proxy Pattern provides a surrogate or placeholder for another object to control access to it

  - Design Patterns: Elements of Reusable Object Oriented Sofare

  在 GoF 的书中 对 Proxy 模式的定义为 替某个对象 提供一个替身 以控制外界对这个对象的访问 而这个被替身代理的对象 (被代理者) 可能是远端的对象 创建时需要高成本或大计算量的对象 或需要安全控制的对象

  

  图  此图为 Proxy 模式的经典类图

   _Shell / Program cs

  using System;

  namespace _ _Shell

  

  //用户端程序

  class Program

  

  static void Main(string[] args)

  

  //用户把 Proxy (代理者) 视为 RealSubject (真实的对象) 来操作

  Proxy proxy = new Proxy();

  proxy Request();

  Console Read();

  

  

  //代理者 被代理者共同实现的接口

  abstract class Subject

  

  public abstract void Request();

  

  //被代理者 真实的对象 把复杂性封装在此

  class RealSubject : Subject

  

  //真正做事的方法 把复杂性封装在此 用户无须知道细节

  public override void Request()

  

  Console WriteLine( 真实的请求 );

  

  

  //代理者

  class Proxy : Subject

  

  RealSubject realSubject;

  public override void Request()

  

  if (realSubject == null)

  

  realSubject = new RealSubject();

  

  realSubject Request();

  

  

  

  /*

  执行结果

  真实的请求

  */

  上方图 的 Class Diagram 以及「Shell (壳)」示例中 我们有一个 Subject 抽像类 这是 RealSubject 和 Proxy 共同的接口 好让任何用户都可将 Proxy 对象 (代理者) 视为 RealSubject 对象 (被代理者 亦即真实的对象) 来处理

  其中 RealSubject 是真正做事的对象 它是被 Proxy 代理的对象 它的方法是真正做事的函数 并会将一些复杂的工作封装在其方法里 而无须让客户端程序知道实现细节为何 而 Proxy 中的同名称方法 (Request 方法) 则可做一些逻辑判断 比如上例中 我们做了 realSubject 是否为 null 的 if 判断 亦即只有客户程序第一次调用此函数时 才去创建 RealSubject 对象

  客户端和 RealSubject 的互动 都必须透过 Proxy 也由于 Proxy 和 RealSubject 实现了相同的接口 所以客户在任何需要 RealSubject 的地方 都可以用 Proxy 取而代之 此外 Proxy 也控制了客户端对 RealSubject 的访问 其目的如同本帖一开始所说的 因为 RealSubject 可能是网络上远端机器上的对象 创建时需要高成本 或需要安全控管及经过认证才能访问的对象 其原理类似于下图 的 HTTP Proxy Server 情景 Client side 想要前往 Internet 取得 Web Server 上的信息时 可透过 Proxy Server 帮忙处理 其中 Web Server 就如同 RealSubject (被代理者) 而 Proxy Server 就如同 Proxy (代理者)

  

  图  基于代理的远程访问示意图

  Proxy Pattern 依照功能和目的 运行环境的不同 可概略分成以下几种

  远程代理 (Remote Proxy) 替网络上机器与机器之间的请求 (request) 做「发送 / 接收」和编码 加密…等工作 让用户端程序 只要调用这个代理就能做远端调用 如 Java RMI Web Service NET Remoting NET WCF 其中 Web Service 和 WCF 会在引用的客户端程序中 产生 App_WebReferences 文件夹 一些档案和代理类 这些档案即为此种远程代理

  保护代理 (Protection Proxy; Access Proxy) 检查调用者是否有权限 去访问真实的对象 例如用户是否有输入正确的密码以通过认证

  智能引用代理 (Smart Reference Proxy) 当对象被调用时 提供一些额外的操作 例如 记录对象被调用的次数

  虚拟代理 (Virtual Proxy) 让一个资源消耗较大的对象 只有在需要时才会真正被创建 或让真实对象只有在第一次被调用时才创建

  其他 例如 Copy On Write Proxy Cache Proxy Firewall Proxy Synchronization Proxy 等 [ ] [ ] [ ]

  在 GoF 中所举的例子是 Virtual Proxy 举了一个文档中内嵌图片的范例 [ ] [ ] 若图片是在文档 (如 PDF PowerPoint) 的其中某一页 用户刚打开文档时 并不需要载入图片 可先用一个 ImageProxy 代替真实的图片被载入 当用户滚动滚动条 转到文档特定的页数时 才真正从硬盘载入图片 以求开启此文档时能加快速度 让用户对此软件有较好的体验

  

  图  虚拟代理 (Virtual Proxy) 在 GoF 示例的类图 与本帖图 的原理相同

  如上图 及下方代码所示 当文档被开启时 ImageProxy 对象会代替 (代理) Image 对象被载入 在用户还没转到图片所在的页数时 也就是还没调用 ImageProxy 的 draw 方法时 图片并不会被载入 因此可加速文档的开启 节省内存的使用 当用户转到图片所在页数时 ImageProxy 的 draw 方法才会被调用 此时才真正去创建 Image 对象 从硬盘中载入图片 在此例的 draw 方法里 我们实现了「虚拟代理」 只有在方法「第一次」被调用时 才创建资源消耗大的 Image 对象 以节省内存 控制创建成本昂贵的资源

   _VirtualProxy_Image / Program cs

  using System;

  using blogs WizardWu Sample ;

  //客户端程序

  class Program

  

  static void Main(string[] args)

  

  //当文档被打开时 ImageProxy 对象会代替(代理) Image 对象被载入

  IGraphic image = new ImageProxy( 风景图 jpg );

  Console WriteLine( 文档被打开 真实的图片尚未被载入 );

  Console ReadLine();

  Console WriteLine( 用户已把滚动条滚到特定页数 图片此时才从硬盘载入 );

  image draw();          //真正要显示图片了

  Console ReadLine();

  

  

  //服务器端程序

  namespace blogs WizardWu Sample

  

  //此为「代理者 被代理者」共同的接口

  public interface IGraphic

  

  //用来画图的方法

  void draw();

  

  //被代理者 真实的对象 把复杂性封装在此

  public class Image : IGraphic

  

  private byte[] data;

  public Image(String fileName)             //构造函数

  

  //载入图片 把复杂性封装起来

  //data = loadImage(fileName);

  Console WriteLine( 开始载入图片 );

  

  public void draw()

  

  //绘制图片在屏幕上 把复杂性封装起来

  //drawToScreen(data);

  Console WriteLine( 图片已成功绘制在屏幕上 );

  

  

  //代理者

  public class ImageProxy : IGraphic

  

  private String fileName;

  private Image image;

  public ImageProxy(String filename)     //构造函数

  

  this fileName = filename;

  image = null;

  

  //等到真正要显示图片了 这个 ImageProxy 的 draw 方法才会被调用

  //此时才会真正去创建 Image 对象 并从硬盘中载入图片

  public void draw()

  

  //虚拟代理 仅在方法第一次被调用时 才创建资源消耗大的 Image 对象

  if (image == null)

  

  image = new Image(fileName);

  

  //实际去绘制图片在萤幕上

  image draw();

  

  

   // end of namespace

  /*

  执行结果

  文档被开启 真实的图片尚未被载入

  用户已把滚动条滚到特定页数 图片此时才从硬盘载入

  开始载入图片

  图片已成功绘制在屏幕上

  */

  代理的主要目的之一 是把复杂性封装起来 让客户端程序在引用上更容易 而不需要顾虑藏在身后这些复杂的逻辑 如上方这个示例 我们可以把「载入图片 绘制图片在屏幕上」这些较复杂的 NET API 引用代码 都封装在「被代理者」这个真实 Image 对象的几个自定义方法里

  此外 我们也可用相同于本示例的逻辑 去实现「保护代理 (Protection Proxy)」的观念 例如要求用户必须输入正确的密码 先通过认证 (Authentication) 后 才能访问 RealSubject 对象 调用其方法 相关的范例 有兴趣的读者可自行去「C# Design Patterns」这本书籍的 O Reilly 英文官方网站上 [ ] 下载第二章的源代码

  接下来的第三个示例 为「数据访问代理」的 ASP NET 例子 [ ] [ ] 用来取得 Northwind 数据库中 Employees 表的记录总数 其类图如下图 和本帖第一 第二个示例的类图略有不同 它是将 RealSubject Proxy 合而为一 变成单一个 DbCommandProxy 类 其左侧的 DbContext 类只是用来协助解决复杂性的问题 包括取得 nfig 的数据库连接字符串 建立数据库的连接

  这个 DbCommandProxy 类 实现了 NET 用来执行 SQL 语句的原生 IDbCommand 接口 [ ] 我们为了将复杂性问题 具体的数据库连接方式隔离出来 因此另外提供了一个 DbContext 类 并将这些动作都搬移至 DbContext 类去处理 以另一种设计理念实现了 Proxy Pattern

  

  图  示例 _DbProxy / Default aspx cs 的类图

   _DbProxy / Default aspx cs

  using System;

  using blogs WizardWu Sample ;

  using System Data;

  using System Data Common;

  using System Configuration;

  //客户端程序

  public partial class _Default : System Web UI Page

  

  protected void Page_Load(object sender EventArgs e)

  

  IDbCommand mand = new DbCommandProxy();

  mand CommandText = SELECT COUNT(*) FROM Employees ;

  //显示 Employees 表的记录总数

  this Label Text = mand ExecuteScalar() ToString();

  

  

  //服务器端程序

  namespace blogs WizardWu Sample

  

  //这个类并非「被代理者」 此为自定义 DbCommandProxy 类的辅助类

  //将具体建立数据库连接…等 较复杂的部分提取出来 隔离在这个类里去处理

  class DbContext

  

  private string strProviderName;

  private string strConnectionString;

  public DbContext(string name)        //构造函数

  

  //取得 nfig 里的数据库连接字符串名称

  ConnectionStringSettings setting = ConfigurationManager ConnectionStrings[ ConnString_SqlClient ];

  this strProviderName = setting ProviderName;

  this strConnectionString = setting ConnectionString;

  

  public DbConnection CreateConnection()

  

  DbProviderFactory factory = DbProviderFactories GetFactory(this strProviderName);

  DbConnection connection = factory CreateConnection();

  connection ConnectionString = this strConnectionString;

  return connection;

  

  

  //将「代理者 被代理者」合而为一 (另一种做法)

  //此类同时为 RealSubject 和 Proxy 的类

  public class DbCommandProxy : IDbCommand

  

  private DbContext context;

  private string strCommandText;

  public DbCommandProxy(string name)                 //构造函数

  

  if (string IsNullOrEmpty(name)) throw new ArgumentNullException( name );

  ntext = new DbContext(name);

  

  public DbCommandProxy() : this( default )      //构造函数

  public object ExecuteScalar()

  

  using (DbConnection connection = context CreateConnection())

  

  connection Open();

  DbCommand mand = connection CreateCommand();

  mand CommandText = this strCommandText;

  mand CommandType = CommandType Text;

  return mand ExecuteScalar();

  

  

  public string CommandText

  

  get return this strCommandText;

  set this strCommandText = value;

  

  //IDbCommand  接口未被实现的成员

  

   // end of namespace

  

  Proxy Pattern 适用的情景

  对象创建的代价比较高

  仅在操作被请求时创建对象

  对象需要访问控制 如: 权限验证 或访问的同时去执行检查或簿记工作

  需要访问远程站点

  被访问时 需要执行一些逻辑判断的动作

  Proxy Pattern 的优点

  降低对象使用的复杂度

  增加对象使用的友好度

  提高程序的效率和性能 (如同 HTTP 的 Proxy Server)

  Proxy Pattern 的缺点

  和一些 Pattern 一样 Proxy Pattern 会造成系统设计中 类的数量增加

  Proxy Pattern 的其他特性

  Proxy Pattern 的结构 类似上一篇帖子的 Decorator Pattern (装饰模式) 但是目的不同

  Decorator Pattern 替对象加上行为 而 Proxy Pattern 则是控制对象的访问

  Proxy Pattern 的关系是在设计阶段就确定好了的 是事先知道的 而 Decorator Pattern 却可以动态地添加

cha138/Article/program/net/201311/11289

相关参考

知识大全 C# Design Patterns (3) - Decorator

C#DesignPatterns(3)-Decorator  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一

炭循环微电解反应器处理腈纶废水的Design-Expert设计优化

[摘要]利用Design-Expert7.1试验设计系统对炭循环微电解反应器处理腈纶废水的试验进行了试验设计和试验结果分析;通过试验设计分析得出了腈纶废水COD去除率与试验影响因素之间的定量关系模型,

炭循环微电解反应器处理腈纶废水的Design-Expert设计优化

[摘要]利用Design-Expert7.1试验设计系统对炭循环微电解反应器处理腈纶废水的试验进行了试验设计和试验结果分析;通过试验设计分析得出了腈纶废水COD去除率与试验影响因素之间的定量关系模型,

炭循环微电解反应器处理腈纶废水的Design-Expert设计优化

[摘要]利用Design-Expert7.1试验设计系统对炭循环微电解反应器处理腈纶废水的试验进行了试验设计和试验结果分析;通过试验设计分析得出了腈纶废水COD去除率与试验影响因素之间的定量关系模型,

知识大全 C#使用BerkeleyDB操作简介[4]

C#使用BerkeleyDB操作简介[4]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 

知识大全 C# 制作以动画的方式显示图像[4]

C#制作以动画的方式显示图像[4]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! mat

知识大全 详解C# 4.0中的新对象ExpandoObject

详解C#4.0中的新对象ExpandoObject  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 

知识大全 .NET 4将对C#和VB一视同仁

.NET4将对C#和VB一视同仁  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在PDC中微软宣

知识大全 C# 4.0中泛型协变性和逆变性详解

C#4.0中泛型协变性和逆变性详解  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  VS的推出会为

知识大全 求英语大神翻译,急

求英语大神翻译,急!hello,guys,iamchengyang,thememberoftheVisualCommunicationDesign132.ihavemanyhobbies,suchas