《设计模式之禅》学习笔记(十二)
第12章 代理模式
代理模式(Proxy Pattern)的定义如下:
Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)
代理模式也叫做委托模式,它是一种基本设计技巧。
该模式下三种角色的定义:
- Subject抽象主题角色
抽象类或接口,是一个最普遍的业务类型定义
- RealSubject具体主题角色
被委托角色,是业务逻辑的具体执行者
- Proxy代理主题角色
委托类,负责对真实角色的应用,并负责预处理和善后工作。
通用源代码:
抽象主题类
public interface Subject {
// 定义一个方法
public void request();
}
真实主题类
public class RealSubject implements Subject {
// 实现方法
public void request() {
// 业务逻辑处理
}
}
代理类
public class Proxy implements Subject {
// 要代理哪个实现类
private Subject subject = null;
// 默认被代理者
public Proxy() {
this.subject = new RealSubject();
}
// 通过构造函数传递代理者
public Proxy(Subject subject) {
this.subject = subject;
}
// 实现接口中定义的方法
public void request() {
this.before();
this.subject.request();
this.after();
}
// 预处理
private void before() {
// do something
}
// 善后处理
private void after() {
// do something
}
}
代理模式的优点:
- 职责清晰。真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的食物,通过后期的代理完成一件事物,附带的结果就是编程简洁清晰。
- 高扩展性。代理类可以在不做任何修改的情况下适应具体主题角色的变化。
- 智能化。动态代理可以实现智能化。
代理模式的扩展:
普通代理
在网络上代理服务器设置分为透明代理和普通代理,是什么意思呢?透明代理就是用户不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说是透明的,不用知道它的存在;普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理的存在。设计模式中的普通代理就是我们要知道代理的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的。
普通代理的要求就是客户端只能访问代理角色,而不能访问真实角色。在该模式下,调用者只知道代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响。该模式非常适合对扩展性要求较高的场合。
public class Client {
public static void main(String[] args) {
// 定义一个带练者
IGamePlayer proxy = new GamePlayerProxy("张三");
System.out.println("开始时间是:" + new Date());
proxy.login("zhangsan", "password");
proxy.killBoss();
proxy.upgrade();
System.out.println("结束时间是:" + new Date());
}
}
public class GamePlayer implements IGamePlayer {
private String name = "";
public GamePlayer(IGamePlayer gamePlayer, String name) throws Exception {
if (gamePlayer == null) {
throw new Exception("不能创建真实角色!");
} else {
this.name = name;
}
}
@Override
public void login(String user, String password) {
System.out.println("登录名为" + user + "的用户" + this.name + "登录成功!");
}
@Override
public void killBoss() {
System.out.println(this.name + "在打怪");
}
@Override
public void upgrade() {
System.out.println(this.name + "又升了一级");
}
}
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(String name) {
try {
this.gamePlayer = new GamePlayer(this, name);
} catch (Exception ex) {
}
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
}
}
强制代理
一般的思维都是通过代理找到真实的角色,但是强制代理却是要"强制",你必须通过真实角色查找到代理角色,否则不能访问。
public class Client {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("张三");
IGamePlayer proxy = player.getProxy();
System.out.println("开始时间是:" + new Date());
proxy.login("zhangSan", "password");
proxy.killBoss();
proxy.upgrade();
System.out.println("结束时间是:" + new Date());
}
}
public class GamePlayer implements IGamePlayer {
private String name = "";
private IGamePlayer proxy = null;
public GamePlayer(String name) {
this.name = name;
}
@Override
public IGamePlayer getProxy() {
this.proxy = new GamePlayerProxy(this);
return this.proxy;
}
@Override
public void killBoss() {
if (this.isProxy()) {
System.out.println(this.name + "在打怪!");
} else {
System.out.println("请使用指定的代理访问");
}
}
@Override
public void login(String user, String password) {
if (this.isProxy()) {
System.out.println("登录名为" + user + "的用户" + this.name + "登录成功!");
} else {
System.out.println("请使用指定的代理访问");
}
}
@Override
public void upgrade() {
if (this.isProxy()) {
System.out.println(this.name + "又升了一级!");
} else {
System.out.println("请使用指定的代理访问");
}
}
private boolean isProxy() {
if (this.proxy == null) {
return false;
} else {
return true;
}
}
}
public class GamePlayerProxy implements IGamePlayer, IProxy {
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
}
@Override
public void login(String user, String password) {
this.gamePlayer.login(user, password);
}
@Override
public void killBoss() {
this.gamePlayer.killBoss();
}
@Override
public void upgrade() {
this.gamePlayer.upgrade();
this.count();
}
@Override
public IGamePlayer getProxy() {
return this;
}
@Override
public void count() {
System.out.println("升级总费用是:150元");
}
}
虚拟代理
虚拟代理就是在需要的时候才初始化主题对象,可以避免被代理对象较多引起的初始化缓慢的问题。其缺点是需要在每个方法中判断主题对象是否被创建。这就是虚拟代理,非常简单。
public class Proxy implements Subject {
private Subject subject = null;
@Override
public void request() {
if (subject == null) {
subject = new RealSubject();
}
subject.request();
}
}
动态代理
动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。
动态代理实现代理的职责,业务逻辑实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。对于日志、事务、权限等都可以在系统设计阶段不用考虑,而在设计后通过动态代理的方式加过去。
public class Client {
public static void main(String[] args) {
// 定义一个主题
Subject subject = new RealSubject();
// 定义一个Handler
InvocationHandler handler = new MyInvocationHandler(subject);
// 定义主题的代理
Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
// 定义的行为
proxy.doSomething("Finish!");
}
}
public class MyInvocationHandler implements InvocationHandler {
// 被代理的对象
private Object target = null;
// 通过构造函数传递一个对象
public MyInvocationHandler(Object obj) {
this.target = obj;
}
// 代理方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Object result = method.invoke(this.target, args);
return result;
}
}
public class DynamicProxy<T> {
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
// 寻找JoinPoint连接点,AOP框架使用元数据定义
if (true) {
// 执行一个前置通知
(new BeforeAdvice()).exec();
}
// 执行目标,并返回结果
return (T) Proxy.newProxyInstance(loader, interfaces, h);
}
}
// 抽象主题
public interface Subject {
// 业务操作
public void doSomething(String str);
}
// 真实主题
public class RealSubject implements Subject {
@Override
public void doSomething(String str) {
System.out.println("do something!--->" + str);
}
}
public interface IAdvice {
// 通知只有一个方法,执行即可
public void exec();
}
public class BeforeAdvice implements IAdvice {
@Override
public void exec() {
System.out.println("我是前置通知,我被执行了!");
}
}