首页 > 其他分享 >代理模式(Proxy)

代理模式(Proxy)

时间:2024-01-13 20:26:16浏览次数:24  
标签:return double 代理 模式 proxy ._ public Proxy

代理模式(Proxy)

 

1.1.1 摘要

     今天是父亲节,首先祝爸爸父亲节快乐身体健康,随着互联网飞速的发展,现在许多软件系统都提供跨网络和系统的应用,但在跨网络和系统应用时,作为系统开发者并不希望客户直接访问系统中的对象。其中原因很多考虑到系统安全和性能因素,这时候聪明的开发者想到了在客户端和系统端添加一层中间层----代理层,也是即将要介绍的代理模式。

 

  • 定义

代理模式(Proxy)为另一个对象提供一个替身或占位符以控制对这个对象的访问,简而言之就是用一个对象来代表另一个对象。

  • 意图

提供其他对象一个代理或占位符,来控制该对象的访问权限。

  • 动机

      为什么我们要控制对象的访问权限呢?其中一个原因是通过控制来延迟对象的创建和实例化,直到真正需要使用该对象才进行创建和实例化。由于一些对象创建和实例化需要占用大量系统资源,但我们并不能确定用户一定会调用该对象,所以通过延迟对象实例化来减缓系统资源的消耗。例如文档编辑器如word,我们可以在里面插入链接、图片等,但是并不是我们每次打开word时都有创建和实例化这些对象,特别是实例化图片对象很消耗资源,而且我们有必要实例化所有图片吗?当我们在查看word时,只是看到其中的一部分,所以没有必要实例化所以资源,当我们看下一页时再实例化也不迟。

  • 结构图clip_image001

图1代理模式结构图

1.1.2 正文

      软件系统设计可以提供本地或远程的方法,随着互联网的发展特别是WebService技术的提出,使得更多软件系统都提供远程方法调用。当我们访问网络上一台计算机的资源时,我们正在跨越网络障碍,跨越网络障碍有时候是非常复杂,因为要确保数据安全可靠地传输。如果真的要我们都去解决那些复杂网络问题,那么我估计程序员们疯了。还好代理模式(Proxy)帮我们解决了其中的一些问题----WebService技术。

     现在让我们通过一个简单的加减乘除程序为例,说明什么是代理模式(Proxy)和如何实现。

     现在我们服务器端提供计算方法,分别定义计算类Math和代理类MathProxy。然后我们的客户端通过调研MathProxy来间接用Math类的计算方法。

/// <summary>
/// Define a interface, make proxy and subject
/// have the same methods.
/// </summary>
public interface IMath
{
    /// <summary>
    /// Calc methods.
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    double Add(double x, double y);
    double Sub(double x, double y);
    double Mul(double x, double y);
    double Div(double x, double y);
}


/// <summary>
/// A proxy class.
/// </summary>
public class MathProxy : IMath
{
    /// <summary>
    /// MathProxy has a Math's reference.
    /// </summary>
    private Math _math;

    public MathProxy()
    {
        AppDomain ad =
            AppDomain.CreateDomain("MathDomain", null, null);

        /// Create a Math object.
        ObjectHandle obj = ad.CreateInstance(
            "Gof.DesignPattern.Proxy.Math",
            "Gof.DesignPattern.Proxy.Math.Math");

        this._math = obj.Unwrap() as Math;
    }

    #region IMath 成员

    public double Add(double x, double y)
    {
        return _math.Add(x, y);
    }

    public double Sub(double x, double y)
    {
        return _math.Sub(x, y);
    }

    public double Mul(double x, double y)
    {
        return _math.Mul(x, y);
    }

    public double Div(double x, double y)
    {
        return _math.Div(x, y);
    }

    #endregion

}

/// <summary>
/// Due to Math inherits MarshalByRefObject
/// so the class supports remoting.
/// </summary>
public class Math : MarshalByRefObject, IMath
{
    #region IMath 成员

    public double Add(double x, double y)
    {
        return x + y;
    }

    public double Sub(double x, double y)
    {
        return x - y;
    }

    public double Mul(double x, double y)
    {
        return x * y;
    }

    public double Div(double x, double y)
    {
        if (y == 0)
        {
            return 0;
        }
        
        return x / y;
    }

    #endregion
}


public class Program
{
    static void Main(string[] args)
    {
        
        // Create math proxy.
        MathProxy prox = new MathProxy();

        //
        Console.WriteLine(prox.Add(2, 6));
        Console.WriteLine(prox.Sub(88, 2));
        Console.WriteLine(prox.Mul(22, 55));
        Console.WriteLine(prox.Div(5, 0));

        Console.ReadKey();
    }

}
 proxy1

图2网络中代理模式

      现在我们实现了Math类的代理模式,使得客户端通过MathProxy代理类来调用Math提供的Add()、Sub()、Mul() 和Div() 方法。

      但细心的你肯定发现问题了,我的程序根本没有提供夸网络的实现,最多就是夸应用程序的调用,的确我并没有实现跨网络的调用,那我们该如何实现跨网络呢?还记得我们前面介绍的WebService技术吗?要实现跨网络可以选择使用WebService来公开我们Math类中的方法,让客户端添加Math类的Web引用,从而WebService充当了代理角色。

     我们前面提到的图片延迟加载的例子,这其中也是体现了代理模式的思想。例如我们在查看一些网页之后,我们的浏览器会保留一些网页的信息,从而加快下次网页加载的速度。现在让我们通过一个简单WinForm图片加载器程序说明:

     图片加载器首先查看我们本地是否已经存在要加载的图片,如果存在就直接加载图片,如果不存在它会到网络中下载图片然后进行加载。

proxy2

图3图片加载器界面

      由于时间的关系我们已经把界面设计好了,现在我们要完成的是点击Test Image Proxy按钮的逻辑,首先我们图片加载器会查看本地是否已经存在图片,如果不存在就到网络上加载我们需要的图片就OK了。

/// <summary>
/// The image proxy
/// If we have dowload the picture in local,
/// then the program would get it, otherwise
/// the program would get the picture in internet.
/// </summary>
private class ImageProxy
{
    private static Image _image = null;

    private int _width = 123;
    private int _height = 154;
    private bool _retrieving = false;

    public int Width
    {
        get { return _image == null ? _width : _image.Width; }
    }

    public int Height
    {
        get { return _image == null ? _height : _image.Height; }
    }

    /// <summary>
    /// Gets the image.
    /// </summary>
    public Image Image
    {
        get 
        {
            if (_image != null)
            {
                return _image;
            }
            else
            {
                if (!_retrieving)
                {
                    _retrieving = true;
                    //// Initialize a thread.
                    Thread retrieveThread = new Thread(
                        new ThreadStart(RetrieveImage));
                    //// Start thread.
                    retrieveThread.Start();
                }
            }
            //// If the picture in local, we get it directly.
            return PlaceHolderImage(); 
        }
    }

    /// <summary>
    /// Places the holder image.
    /// Get the picture in local.
    /// </summary>
    /// <returns></returns>
    public Image PlaceHolderImage()
    {
        return new Bitmap(System.Reflection.Assembly.GetExecutingAssembly().
            GetManifestResourceStream(this.GetType().Namespace.ToString() + ".googlefathersday.jpg"));
    }


    /// <summary>
    /// Retrieves the image.
    /// Get the picture in internet.
    /// </summary>
    public void RetrieveImage()
    {
        string url = @"http://www.google.com.hk/logos/2011/fathersday11-hp.jpg";

        HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
        HttpWebResponse response = request.GetResponse() as HttpWebResponse;

        _image = Image.FromStream(response.GetResponseStream());

    }
}

     上面我们完成了点击加载的逻辑,我们提供一个图片加载代理ImageProxy类,然后分别提供本地加载和网络加载功能PlaceHolderImage()方法和RetrieveImage()方法,PlaceHolderImage()方法把图片嵌入到程序中,而RetrieveImage()到网络中加载图片。

 

       
proxy3proxy4

图4图片加载效果

    这里我们使用了灰色和彩色图片分别表示本地和网络加载效果,现在我们通过代理模式(Proxy)完成了图片延迟加载的效果。

    我觉得《HeadFirst设计模式》中的糖果机代理模式比较有趣,现在也让我们通过C#来实现糖果机代理模式。

    问题描述:现在有些分布在不同地方的糖果机(类似自动售卖机),我们要通过网络方式获得糖果机中的糖果数量和糖果机的状态。

    现在我们要添加三个项目分别是:GumballState.Machine、Host和Client(其中Host是WebService),在GumballState.Machine里面我们定义糖果机售卖逻辑和状态,Host把我们定义售卖机方法通过网络形式发布,Client通过添加Web引用间接调用GumballState.Machine中的方法。

proxy5

图5糖果机程序

 

    首先我们GumballState.Machine类添加五个方法分别是:StartWithQuarters()、InsertQuarter()、TurnCrank()、EjectQuarter()和GetReport()。

  • StartWithQuarters()方法初始化糖果数量和修改糖果机状态。
/// <summary>
/// Starts the with quarters.
/// Calc the current qty and set machine state.
/// </summary>
/// <param name="cnt">The CNT.</param>
public void StartWithQuarters(int cnt)
{
    this._cnt = cnt;
    if (this._cnt > 0)
    {
        this._state = GumballMachineState.NoQuarter;
    }
}
  • InsertQuarter()方法判断用户付款操作和修改糖果机状态。
/// <summary>
/// Inserts the quarter.
/// The user inserts money
/// </summary>
public void InsertQuarter()
{
    switch (this._state)
    {
        case (GumballMachineState.HasQuarter):
            this._logMsg.Append("You can't insert another quarter\n");
            return;

        case (GumballMachineState.NoQuarter):
            this._state = GumballMachineState.HasQuarter;
            this._logMsg.Append("You inserted a quarter\n");
            return;

        case (GumballMachineState.SoldOut):
            this._logMsg.Append("You can't insert a quarter, the machine is sold out\n");
            return;

        case (GumballMachineState.Sold):
            this._logMsg.Append("Please wait, we're already giving you a gumball\n");
            return;

    }
}
  • TurnCrank()方法判断用户确定购买操作和修改糖果机状态。
    /// <summary>
    /// Turns the crank.
    /// The user presses "OK" button to get gumball.
    /// </summary>
    public void TurnCrank()
    {
        if (this._state == GumballMachineState.SoldOut)
        {
            this._logMsg.Append("You turned, but there are no gumballs\n");
        }
        else if (this._state == GumballMachineState.HasQuarter)
        {
            this._logMsg.Append("You turned please wait...\n");
            this._state = GumballMachineState.Sold;
            this.Dispense();
        }
        else if (this._state == GumballMachineState.NoQuarter)
        {
            this._logMsg.Append("You turned but there's no quarter\n");
        }
        else
        {
            this._logMsg.Append("Turning twice doesn't get you another gumball!\n");
        }
    }
  • EjectQuarter()方法判断用户退款操作和修改糖果机状态。
/// <summary>
/// Ejects the quarter.
/// The user cancels order and get money back.
/// </summary>
public void EjectQuarter()
{
    switch (this._state)
    {
        case (GumballMachineState.HasQuarter):
            this._state = GumballMachineState.NoQuarter;
            this._logMsg.Append("Quarter returned\n");
            return;

        case (GumballMachineState.NoQuarter):
            this._state = GumballMachineState.HasQuarter;
            this._logMsg.Append("You haven't inserted a quarter\n");
            return;

        case (GumballMachineState.SoldOut):
            this._logMsg.Append("Sorry, you already turned the crank\n");
            return;

        case (GumballMachineState.Sold):
            this._logMsg.Append("You can't eject, you haven't inserted a quarter yet\n");
            return;

    }
}
  • GetReport()方法返回糖果机销售量和状态给管理者。
/// <summary>
/// Gets the report.
/// </summary>
/// <returns></returns>
public string GetReport()
{
    StringBuilder result = new StringBuilder();
    result.Append("\nMighty Gumball, Inc.");
    result.Append("\n.NET3.5-enabled Standing Gumball Model #2104\n");
    result.Append("Inventory: " + this._cnt + " gumball");
    if (this._cnt != 1) result.Append("s");

    result.Append("\nMachine is ");
    if (_state == GumballMachineState.SoldOut)
    {
        result.Append("sold out");
    }
    else if (_state == GumballMachineState.NoQuarter)
    {
        result.Append("waiting for quarter");
    }
    else if (_state == GumballMachineState.HasQuarter)
    {
        result.Append("waiting for turn of crank");
    }
    else if (_state == GumballMachineState.Sold)
    {
        result.Append("delivering a gumball");
    }
    result.Append("\n");

    string ret = this._logMsg.ToString() + "\n" + result.ToString();
    this._logMsg = new StringBuilder();

    return ret.ToString();
}

    接着我们通过WebService把以上方法通过网络形式发布,然后我们在客户端添加web引用来间接调用GumballState.Machine类中方法。

clip_image001

proxy7

图6客户端添加web引用

   现在我们完成了服务器端的功能了,接下来我们通过控制台应用程序作为客户端调用服务器端公开的方法。

class Program
{
    static void Main()
    {
        // Create proxy object
        GumballMachineClient proxy = new GumballMachineClient();

        proxy.StartWithQuarters(5);
        proxy.InsertQuarter();
        proxy.TurnCrank();

        Console.WriteLine(proxy.GetReport());

        proxy.InsertQuarter();
        proxy.EjectQuarter();
        proxy.TurnCrank();

        Console.WriteLine(proxy.GetReport());

        proxy.InsertQuarter();
        proxy.TurnCrank();
        proxy.InsertQuarter();
        proxy.TurnCrank();
        proxy.EjectQuarter();

        Console.WriteLine(proxy.GetReport());

        proxy.InsertQuarter();
        proxy.InsertQuarter();
        proxy.TurnCrank();
        proxy.InsertQuarter();
        proxy.TurnCrank();
        proxy.InsertQuarter();
        proxy.TurnCrank();

        Console.WriteLine(proxy.GetReport());

        // Wait for user
        Console.ReadKey();
    }
}

图7客户端调用web方法

      现在我们通过控制台应用程序添加web引用之后,来调用GumballState.Machine类中方法,我们终于正在实现了一个跨网络的应用程序调用,WebService充当了proxy的角色,而且帮我们解决了很多网络的问题使得我们实现跨网络程序十分方便。

1.1.3 总结

   代理模式(Proxy)根据种类不同,效果也不尽相同:

  • 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部份的网络通讯工作。由于客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
  • 虚拟(Virtual)代理(图片延迟加载的例子):根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载;代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的情况下,虚拟代理的好处就非常明显。
  • Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
  • 智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

 

     代理模式(Proxy)VS 装饰者(Decorator)

     意图:它们都提供间接访问对象层,都保存被调用对象的引用。

     代理模式(Proxy):为另一个对象提供一个替身或占位符以控制对这个对象的访问,简而言之就是用一个对象来代表另一个对象。

    装饰者(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,Decorator模式比生成子类更为灵活,它避免了类爆炸问题,像装饰者(Decorator),代理模式(Proxy)组成一个对象并提供相同的接口,但代理模式并不关心对象动态职能的增减。

    在代理模式(Proxy)中Subject定义了主要的功能,而且Proxy根据Subject提供功能控制对象的访问权限。在装饰者(Decorator)中Component只是提供了其中的一些功能,需要通过装饰链动态给对象增加职能。

 

代理模式(Proxy)例子源程序

标签:return,double,代理,模式,proxy,._,public,Proxy
From: https://www.cnblogs.com/sexintercourse/p/17962862

相关文章

  • AntDesignBlazor示例——暗黑模式
    本示例是AntDesignBlazor的入门示例,在学习的同时分享出来,以供新手参考。示例代码仓库:https://gitee.com/known/BlazorDemo1.学习目标暗黑模式切换查找组件样式覆写组件样式2.添加暗黑模式切换组件1)双击打开MainLayout.razor文件,在header区域添加Switch组件及其事件来......
  • 单例模式(Singleton)的6种实现
    单例模式(Singleton)的6种实现 1.1.1摘要      在我们日常的工作中经常需要在应用程序中保持一个唯一的实例,如:IO处理,数据库操作等,由于这些对象都要占用重要的系统资源,所以我们必须限制这些实例的创建或始终使用一个公用的实例,这就是我们今天要介绍的——单例模式(Singl......
  • 设计模式之中介者模式
    1.定义多个对象之间通过一个中介者对象进行通信和协作,而不是直接相互交互2.口语化表述中介,这在生活中很常见,比如租房中介通常,有住房出租的房东有很多,需要租房的租客也很多,但是租客难以直接联系房东,这个时候租房中介这个职业就出现了房东将房屋登记到中介这里,租客来中介这里......
  • freeswitch: esl inbound模式下外呼拨号
    相信大家可能接到过一些电话,听上去不象是真人打过来的,比如:通知“您的信用卡到期了”,或者“您订的飞机航班取消了,请尽快改签或取消行程”,这种就是所谓的“自动外呼”系统,技术上讲,可以通过eslinbound模式实现(注:对esl不熟悉的朋友,戳这里)大概思路:先把一些要外呼的任务计划,落地存......
  • haproxy笔记
    文章目录场景haproxy配置文档地址场景还得先从场景说起。生产环境redis检查,发现配置的redis地址不对。redis有3个节点。192.168.0.1192.168.0.2192.168.0.3但是配置的是192.168.0.9端口是16379。好奇怪有没有,是不是配错了?问了下部署大神,才确认部署的没问题。说是走的h......
  • C++ 单例模式以及内存管理
    引用:https://zhuanlan.zhihu.com/p/37469260https://www.cnblogs.com/xiaolincoding/p/11437231.htmlhttps://blog.csdn.net/unonoi/article/details/121138176单例模式:一个类在全局范围内只有一个实例化的对象核心:构造函数是私有的,防止外界创建单例类的对象。使用类内的......
  • vim模式用法及命令
    vim的安装[root@ycy2023~]#yuminstallvim-y学习vim编辑器vim命令模式(重点)进入命令模式按a,i,o由命令模式进入编辑模式按ESC键就可以退出编辑模式进入命令模式光标移动按a进入是当前光标所在的字符后光标前输入内容,按i进入是当前光标所在字符前输入内容,按o进入......
  • 新版的Edge浏览器如何设置网络代理?
    这个问题折腾了小半天,通过这种方式希望能帮助他人。版本信息(Linux系统):MicrosoftEdge版本121.0.2277.49(正式版本)beta(64位)根据网上的文档,在“设置”里面既找不到所谓的“高级设置”选项,也找不到所谓的“网络设置”选项,所以压根就找不到设置代理的入口。实在没办法,就自己......
  • 【设计模式】工厂方法模式——工厂方法模式在Android SDK源码中的应用
    工厂方法模式在AndroidSDK源码中有广泛的应用,无论app层还是framework层,无论是Java实现的还是Kotlin实现的。BitmapFactoryBitmap的构造方法都没有被public修饰,开发者必须用BitmapFactory来生成Bitmap对象,BitmapFactory中用于生产Bitmap实例的工厂方法如下:publicclassBitmapFacto......
  • 原型模式
    原型模式是利用克隆方法克隆出新的对象.定义:原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象特点:不需要知道任何创建的细节,不调用构造函数适用场景:类初始化消耗较多资源new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)构造函数比较复......