【设计方式】之代理模式(Proxy)

【设计模式】之代理模式(Proxy)

代理模式的定义为:为其他对象提供一个代理来控制对它的访问。

英文定义:Provide a surrogate or placeholder for another object to control access to it.

这个中文定义没有很好的反映出原本的意思。英文定义中的surrogate与placeholder都有代理的意思。但是他们之间肯定存在微妙的差别。

以下翻译结果,来自有道

surrogate

  • n. 代理;代用品;遗嘱检验法官
  • vt. 代理;指定某人为自己的代理人
  • adj. 代理的;替代的

placeholder

n. 占位符

我的理解是,surrogate是代理中动的一方面,而placeholder则是静的一面。

代理模式有很多分类:
Remote Proxy
    A remote proxy provides a local representative for an object in a difference address space.
    Remote proxies are responsible for encoding a request and its arguments and for sending the encoded request to the real subject in a difference address space.

Virtual Proxy
    A virtual proxy creates expensive objects on demand.
    Virtual proxies may cache additional information about the real subject so that they can postpone accessing it.

Protection Proxy
    A protection proxy controls access to the original object.
    Protection proxies check that the caller has the access permissions required to perform a request.

Smart Reference
    A smart reference is a replacement for a bare pointer that performs additional actions when an object is accessed.

除了以上比较重要的用途外,还有以下类型:

Firewall Proxy
Caching Proxy
Synchronization Proxy
Complexity Hidding Proxy
Copy-On-Write Proxy

可见Proxy的用途是非常广泛的。

Proxy可以与real subject继承自同一基类,这样任何用到proxy的地方,都可已用real subject来代替,
这一点与decorator模式非常相似,但是他们的intent是不同的。

以下例子是利用java提供的Proxy实现的Protection Proxy。

定义subject对应接口
public interface PersonBean {
    String getName();
    String getGender();
    String getInterests();
    int getHotOrNotRating();

    void setName(String name);
    void setGender(String gender);
    void setInterests(String interests);
    void setHotOrNotRating(int rating);
}

然后定义RealSubject
public class PersonBeanImpl implements PersonBean {
    String name;
    String gender;
    String interests;
    int rating;
    int ratingCount = 0;

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender;
    }

    public String getInterests() {
        return interests;
    }

    public int getHotOrNotRating() {
        if (ratingCount == 0) return 0;
        return (rating / ratingCount);
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public void setInterests(String interests) {
        this.interests = interests;
    }

    public void setHotOrNotRating(int rating) {
        this.rating += rating;
        ratingCount++;
    }
}

定义proxy的handler
import java.lang.reflect.*;

public class OwnerInvocationHandler implements InvocationHandler {
    PersonBean person;

    public OwnerInvocationHandler(PersonBean person) {
        this.person = person;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
        throws IllegalAccessException {
        try {
            if (method.getName().startsWith("get")) {
                return method.invoke(person, args);
            } else if (method.getName().equals("setHotOrNotRating")) {
                throw new IllegalAccessException();
            } else if (method.getName().startsWith("set")) {
                return method.invoke(person, args);
            }
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

import java.lang.reflect.*;

public class NonOwnerInvocationHandler implements InvocationHandler {
    PersonBean person;

    public NonOwnerInvocationHandler(PersonBean person) {
        this.person = person;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
        throws Throwable {
        try {
            if (method.getName().startsWith("get")) {
                return method.invoke(person, args);
            } else if (method.getName().equals("setHotOrNotRating")) {
                return method.invoke(person, args);
            } else if (method.getName().startsWith("set")) {
                throw new IllegalAccessException();
            }
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试代码
import java.lang.reflect.*;
import java.util.ArrayList;

public class MatchMakingTestDrive {
    // instance variables here
    ArrayList<PersonBean> persons;

    public static void main(String[] args) {
        MatchMakingTestDrive test = new MatchMakingTestDrive();
        test.drive();
    }

    public MatchMakingTestDrive() {
        initializeDatabase();
    }

    public void drive() {
        PersonBean joe = getPersonFromDatabase("Joe Javabean");
        PersonBean ownerProxy = getOwnerProxy(joe);
        System.out.println("Name is " + ownerProxy.getName());
        ownerProxy.setInterests("bowling, Go");
        System.out.println("Interests set from owner proxy");
        try {
            ownerProxy.setHotOrNotRating(10);
        } catch (Exception e) {
            System.out.println("Can't set rating from owner proxy");
        }
        System.out.println("Rating is " + ownerProxy.getHotOrNotRating());

        PersonBean nonOwnerProxy = getNonOwnerProxy(joe);
        System.out.println("Name is " + nonOwnerProxy.getName());
        try {
            nonOwnerProxy.setInterests("bowling, Go");
        } catch (Exception e) {
            System.out.println("Can't set interests from non owner proxy");
        }
        nonOwnerProxy.setHotOrNotRating(3);
        System.out.println("Rating set from non owner proxy");
        System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating());
    }

    PersonBean getOwnerProxy(PersonBean person) {
        return (PersonBean)Proxy.newProxyInstance(
            person.getClass().getClassLoader(),
            person.getClass().getInterfaces(),
            new OwnerInvocationHandler(person));
    }

    PersonBean getNonOwnerProxy(PersonBean person) {
        return (PersonBean)Proxy.newProxyInstance(
            person.getClass().getClassLoader(),
            person.getClass().getInterfaces(),
            new NonOwnerInvocationHandler(person));
    }

    private void initializeDatabase() {
        persons = new ArrayList<PersonBean>();
        PersonBean personBean = new PersonBeanImpl();
        personBean.setName("Joe Javabean");
        personBean.setGender("male");
        personBean.setInterests("Drinking");
        personBean.setHotOrNotRating(2);
        persons.add(personBean);
    }

    private PersonBean getPersonFromDatabase(String name) {
        for (PersonBean person : persons) {
            if (person.getName().equals(name)) {
                return person;
            }
        }
        return null;
    }
}

运行结果如下:
【设计方式】之代理模式(Proxy)