012.设计模式之Proxy 模式

 一、概述
大家都用过代理服务器,代理服务器是从出发点到目的地之间的中间层。而Proxy模式中的Proxy功能上与此类似,是对象的访问者与对象之间的中间层。
Proxy(代理)模式可用于解决在直接访问对象不方便或不符合要求时,为这个对象提供一种代理,以控制对该对象的访问。

二、结构
Proxy模式的类图结构如下图所示:

1:Proxy模式类图示意
在上面的类图中,Proxy类是Subject类的子类,但个人认为,单纯从Proxy的意图上讲,这一约束不是必须的。

与前面讨论的Decorator模式不同,在应用Proxy模式时,我们可能知道目标,也可能不知道目标(此时,被代理的对象由Proxy类创建),而Decorator模式下我们往往按照访问目标的方式去访问Decorator,即我们总是知道目标,或者说我们更注意的是目标,而不是Decorator。并且,我们Proxy类可能提供与被访问者不同的接口,而Decorator模式下应保证接口的一致性,以便用户可以用与访问Decoratee一样的方式来访问Decorator。
单纯从结构上讲,Adapter模式与Proxy模式比较相似,但二者的区别在于意图的不同:Adapter的意图在于接口的转换,而Proxy的意图在于代理(或控制),因此,有人形象地将Proxy模式称为“票贩子模式”,而将Adapter模式称为“外汇买卖模式”。

三、应用
Proxy这个模式的目的比较笼统,引入Proxy的目的是在Subject和Client之间构建一个中间层,它适用的情况很多,可以简单分成以下几类:
1
、远程访问代理(可能为了简化客户代码,也可能为了集中管理等);
2
、重要对象(可能是共享对象或大的,耗资源的对象)访问代理;
3
、访问控制代理。
在实际应用中,可能出现同时属于以上多种类别的情况。下面是一些可以应用Proxy模式的常见情况:
1
、远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中,远程代理又叫做大使(Ambassador)。常见的应用如CORBA、DCOM等。
2
、虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。如某个Word文档中包含很多较大的图片,需要花费很长时间才能显示出来,那么使用编辑器或浏览器打开这个文档,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片,先在图片的位置显示一个框,然后随着图片逐步被打开,再对显示区域以及内容进行调整,直至显示所需内容。再比如说数据库的显示,客户现在只需要显示1-10条,你放在内存中10000条对客户没有任何意义,这时可以用一个代理,只是取出前10条就可以了,当客户选择显示下10条时再取出接下来的10条数据显示给客户。
3
、Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。这在访问大的对象和防止频繁拷贝造成过多消耗时经常被用到,现代操作系统往往使用这种技术来防止频繁写磁盘,对于我们的普通应用而言,这种技术也被经常使用,当对象未发生修改时,先使用已有的对象,任何一方发生修改时才执行拷贝动作,创建新的对象,以避免在创建对象上过多的消耗(因为我们有可能在整个对象存在期间不会被修改),从而提高处理效率。
4
、保护(Protect or Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。对于硬件等系统资源而言,OS即是一层保护代理。
5
、Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。 
6
、防火墙(Firewall)代理:保护目标,不让恶意用户接近。 
7
、同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。 
8
、智能引用(Smart Reference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。

总之,当需要对Client访问目标对象的行为进行控制时,请尽可能不要通过继承来对目标进行深入规范,而是采用Proxy模式在Client和Subject之间建立一个中间层,这将为我们的系统提供更大的可扩展性。

四、优缺点

五、举例
由于Proxy模式的目的是在Subject和Client之间构建一个中间层,以控制Client对Subject的访问,其应用十分广泛,很难举出一个面面俱到的例子,以下用两个典型应用来简单演示Proxy模式的应用。如果你感兴趣,也可以研究一下STL的auto_ptr和string的实现,其中也用到了Proxy模式,其中,std::string使用了Copy-on-Write技术。

以下示例取自Thinking in C++ 2nd,演示了Copy-on-Write技术的实现方法,耐心的Bruce Eckel用软件工程领域经典的Dog&DogHouse的例子清晰地告诉了我们应该如何实现Copy-on-Write(除了两处注释外,所有代码出自Bruce Eckel)。此外,关于string类中如何运用Copy-on-Write的问题,见参考1

//: C12:ReferenceCounting.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 2000
// Copyright notice in Copyright.txt
// Reference count, copy-on-write
#include <string>
#include <iostream>
using namespace std;

// assistant function
inline void require(bool requirement, 
                    const
 std::string& msg = "Requirement failed"){
    using namespace
 std;
    if
 (!requirement) {
        fputs(msg.c_str(), stderr);
        fputs("/n", stderr);
        exit(1);
    }
}


// Subject class
class Dog {
    string nm;
    int
 refcount;
    Dog(const string& name
        :
 nm(name), refcount(1) {
        cout << "Creating Dog: " << *this << endl;
    }

    // Prevent assignment:
    Dog& operator=(const Dog& rv);
public
:
    // Dogs can only be created on the heap:
    static Dog* make(const string& name) {
        return
 new Dog(name);
    }

    Dog(const Dog& d
        :
 nm(d.nm + " copy"), refcount(1) {
        cout << "Dog copy-constructor: " 
            << *
this << endl;
    }
    ~
Dog() { 
        cout << "Deleting Dog: " << *this << endl;
    }

    void
 attach() { 
        ++
refcount;
        cout << "Attached Dog: " << *this << endl;
    }

    void
 detach() {
        require(refcount != 0);
        cout << "Detaching Dog: " << *this << endl;
        // Destroy object if no one is using it:
        if(--refcount == 0) delete this;
    }

    // Conditionally copy this Dog.
    // Call before modifying the Dog, assign
    // resulting pointer to your Dog*.
    Dog* unalias() {
        cout << "Unaliasing Dog: " << *this << endl;
        // Don't duplicate if not aliased:
        if(refcount == 1) return this;
        --
refcount;
        // Use copy-constructor to duplicate:
        return new Dog(*this);
    }

    void
 rename(const string& newName) {
        nm = newName;
        cout << "Dog renamed to: " << *this << endl;
    }

    friend
 ostream&
        operator
<<(ostream& os, const Dog& d) {
        return
 os << "[" << d.nm << "], rc = " 
            <<
 d.refcount;
    }
};


// Proxy class
class DogHouse {
    Dog* p;
    string houseName;
public
:
    DogHouse(Dog* dog, const string& house)
        :
 p(dog), houseName(house) {
        cout << "Created DogHouse: "<< *this << endl;
    }

    DogHouse(const DogHouse& dh)
        :
 p(dh.p),
        houseName("copy-constructed " + 
        dh.houseName) {
        p->attach();
        cout << "DogHouse copy-constructor: "
            << *
this << endl;
    }

    DogHouse& operator=(const DogHouse& dh) {
        // Check for self-assignment:
        if(&dh != this) {
            houseName = dh.houseName + " assigned";
            // Clean up what you're using first:
            p->detach();
            p = dh.p; // Like copy-constructor
            p->attach();
        }

        cout << "DogHouse operator= : "
            << *
this << endl;
        return
 *this;
    }

    // Decrement refcount, conditionally destroy
    ~DogHouse() {
        cout << "DogHouse destructor: " 
            << *
this << endl;
        p->detach(); 
    }

    void
 renameHouse(const string& newName) {
        houseName = newName;
    }

    void
 unalias() { p = p->unalias(); }
    // Copy-on-write. Anytime you modify the 
    // contents of the pointer you must 
    // first unalias it:
    void renameDog(const string& newName) {
        unalias();
        p->rename(newName);
    }

    // ... or when you allow someone else access:
    Dog* getDog() {
        unalias();
        return
 p
    }

    friend
 ostream&
        operator
<<(ostream& os, const DogHouse& dh) {
        return
 os << "[" << dh.houseName 
            <<
 "] contains " << *dh.p;
    }
};
 

int
 main() {
    DogHouse 
        fidos(Dog::make("Fido"), "FidoHouse"),
        spots(Dog::make("Spot"), "SpotHouse");
    cout << "Entering copy-construction" << endl;
    DogHouse bobs(fidos);
    cout << "After copy-constructing bobs" << endl;
    cout << "fidos:" << fidos << endl;
    cout << "spots:" << spots << endl;
    cout << "bobs:" << bobs << endl;
    cout << "Entering spots = fidos" << endl;
    spots = fidos;
    cout << "After spots = fidos" << endl;
    cout << "spots:" << spots << endl;
    cout << "Entering self-assignment" << endl;
    bobs = bobs;
    cout << "After self-assignment" << endl;
    cout << "bobs:" << bobs << endl;
    // Comment out the following lines:
    cout << "Entering rename(/"Bob/")" << endl;
    bobs.getDog()->rename("Bob");
    cout << "After rename(/"Bob/")" << endl;
    
    return
 0;
}
 ///:~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值