代理模式
代理模式(Proxy Pattern)也叫委托模式,是一个使用率非常高的模式。
1代理模式的定义
代理模式的英文原话是:
Provide a surrogate or placeHolder for another object to control access to it.
意思是:为其他对象提供一种代理以控制对这个对象的访问。
代理模式是一项基本的设计技巧,许多其他模式,如状态模式、策略模式、访问者模式本质上也采用了代理模式。
代理模式提供一下3个角色。
- 抽象主题(Subject)角色:该角色是真实主体和代理主体的共同接口,以便在任何可以使用真实主体的地方都可以使用代理主体。
- 代理主体(Proxy Subject)角色:也叫作委托类、代理类,该角色负责控制对真实主体的引用,负责在需要的时候创建或删除真实主题对象,并且在真实主题角色处理完毕前后座预处理和善后处理工作。
- 真实主题(Real Subject)角色:也叫被委托角色、被代理角色,是业务逻辑具体执行者。
一些比较易于理解的例子:
1.Windows里的快捷方式。
2.猪八戒找高翠兰,结果是孙悟空变的。可以理解为,把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰时看不出来高翠兰这个代理类。
3.买火车票不一定在火车站买,也可以去代售点。
4.一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
5.spring中的aop。
代理模式的类图
创建抽象主题
Subject.java
package com.eric.结构型模式.代理模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 抽象主题类
* @CreateTime 2020-11-28 08:32:13
*/
public interface Subject {
//定义一个请求方法
public void request();
}
创建真实主题
RealSubject.java
package com.eric.结构型模式.代理模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 真实主题
* @CreateTime 2020-11-28 08:33:10
*/
public class RealSubject implements Subject{
@Override
public void request() {
//业务处理
System.out.println("我处理了一个业务...");
}
}
创建代理类
ProxySubject.java
package com.eric.结构型模式.代理模式.引例;
/**
* @author Eric
* @ProjectName my_design_23
* @description 代理主题
* @CreateTime 2020-11-28 08:34:31
*/
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject){
this.subject = subject;
}
//实现请求方法
@Override
public void request() {
this.beforeRequest();
subject.request();
this.afterRequest();
}
//请求前操作
private void beforeRequest()
{
//预处理
System.out.println("代理类的预处理...");
}
//请求后的操作
private void afterRequest()
{
System.out.println("代理类的善后处理...");
}
}
注意事项:
- 与适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
- 与装饰者模式的区别:装饰者模式是为了增强功能,代理模式则是为了加以控制。
2代理模式的应用
a.代理模式的种类
- 远程(Remote)代理:为一个位于不同空间的对象提供一个局部代表对象。这个不同地址空间,可以使本机也可以在另一台机器中。
- 虚拟(Virtual)代理:有时需要创建一些消耗较多资源的对象,可以首先创建代理对象,而将真实对象的创建延迟。例如,加载一个很大的图片,可以通过图片的代理来代替真正的图片。
- 保护(Protect Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
- 缓存(Cache)代理:为一个目标操作的结果提供链式的存储空间,以便多个客户端可以共享这些结果。
- 同步(Sychronization)代理:使几个用户能够同时使用一个对象而没有冲突。
- 智能(Smart Reference)引用:当一个对象被引用时,提供一些额外的操作,例如,记录访问的流量和次数等。
注意: 在所有种类的代理中,虚拟代理、远程代理、智能代理和保护代理是最为常见的代理模式。
b.代理模式的优点
- 职责清晰:真实角色实现实际的业务逻辑,不用关心其他非本职的事物,通过后期的代理完成附加的事物,附带的结果就是编程简洁清晰。
- 高扩展性:具体主体角色随需求不同可能有很多种,但只要实现了接口,代理类就完全可以在不作任何修改的情况下代理各种真实主题角色。
- 智能化:代理类可以在运行时才确定需要去代理的真实主题,这是一种强大的功能。
c.代理模式的使用场景
运用很广泛,大到系统框架、企业平台,小到事务处理、代码片段,随处可见代理模式的使用。例如,java RMI的远程调用就是一种代理模式的应用,现在流行的AOP也可以通过代理模式实现。
3代理模式实例
例1:游戏代练
IGamePlayer接口对游戏玩家进行抽象
IGamePlayer.java
package com.eric.结构型模式.代理模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 游戏接口
* @CreateTime 2020-11-29 18:01:15
*/
public interface IGamePlayer {
public void killBoss();//杀Boss
public void upGrade();//升级
}
GamePlayer实现IGamePlayer接口,是玩家
GamePlayer.java
package com.eric.结构型模式.代理模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 玩家类
* @CreateTime 2020-11-29 18:02:38
*/
public class GamePlayer implements IGamePlayer{
private String name= "";
public GamePlayer(String name){
this.name = name;
}
public void killBoss(){
System.out.println(this.name+"杀Boss中...");
}
public void upGrade(){
System.out.println("成功升了一级!!!");
}
}
代理类则实现IGamePlayer接口,并实现两个私有方法日志和消耗时间方法。
GamePlayerProxy.java
package com.eric.结构型模式.代理模式.例1;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author Eric
* @ProjectName my_design_23
* @description 游戏代练
* @CreateTime 2020-11-29 18:05:44
*/
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer player = null;
public GamePlayerProxy(IGamePlayer player){
this.player = player;
}
//记录打怪时间
private void log(){
String pattern = "YYYY-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
System.out.println("打怪时间: "+sdf.format(new Date()));
}
@Override
public void killBoss() {
this.log();
player.killBoss();
}
@Override
public void upGrade() {
player.upGrade();
this.count();
}
//计算升级所用时间
private void count()
{
System.out.println("升1级耗费100小时!");
}
}
测试
ClientDemo.java
package com.eric.结构型模式.代理模式.例1;
/**
* @author Eric
* @ProjectName my_design_23
* @description 测试类
* @CreateTime 2020-11-29 18:35:46
*/
public class ClientDemo {
public static void main(String[] args) {
IGamePlayer player = new GamePlayer("Eric");
GamePlayerProxy proxy = new GamePlayerProxy(player);
proxy.killBoss();
proxy.upGrade();
}
}
测试结果:
例2:图片的代理
我们将创建一个Image接口和实现了Image接口的实体类。ImageProxy是一个代理类,减少RealImage对象加载的内存占用。
ProxyPatternDemo类使用ProxyImage来获取要加载的Image对象,并按照需求进行显示。
步骤1
创建接口
Image.java
package com.eric.结构型模式.代理模式.例2;
/**
* @author Eric
* @ProjectName my_design_23
* @description 图片接口
* @CreateTime 2020-11-29 19:03:44
*/
public interface Image {
void display();
}
步骤2
创建接口的实现类
RealImage.java
package com.eric.结构型模式.代理模式.例2;
/**
* @author Eric
* @ProjectName my_design_23
* @description 真实的图片
* @CreateTime 2020-11-29 19:04:48
*/
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("正在显示..."+fileName);
}
private void loadFromDisk(String fileName){
System.out.println("从硬盘加载中..."+fileName);
}
}
ImageProxy.java
package com.eric.结构型模式.代理模式.例2;
/**
* @author Eric
* @ProjectName my_design_23
* @description 图片代理类
* @CreateTime 2020-11-29 19:25:24
*/
public class ImageProxy implements Image {
private RealImage realImage;
private String fileName;
public ImageProxy(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null)
realImage = new RealImage(fileName);
realImage.display();
}
}
步骤三
测试
package com.eric.结构型模式.代理模式.例2;
/**
* @author Eric
* @ProjectName my_design_23
* @description 测试类
* @CreateTime 2020-11-29 19:28:26
*/
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ImageProxy("test01.jpg");
//图片从硬盘加载
image.display();
System.out.println("--------------------");
//图片不需要从硬盘加载
image.display();
}
}
测试结果
可以看出第一次从硬盘中加载了图片,而第二次则是从缓存中加载图片。