类型:结构型模式
要点:实现与被代理类组合。
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
介绍
意图: 为其他对象提供一种代理以控制对这个对象的访问。
主要解决: 在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用: 想在访问一个类时做一些控制。
如何解决: 增加中间层。
关键代码: 实现与被代理类组合。
应用实例:
- Windows 里面的快捷方式。
- 猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。
- 买火车票不一定在火车站买,也可以去代售点。
- 一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
- spring aop。
优点:
- 职责清晰。
- 高扩展性。
- 智能化。
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:
按职责来划分,通常有以下使用场景:
- 远程代理。
- 虚拟代理。
- Copy-on-Write 代理。
- 保护(Protect or Access)代理。
- Cache代理。
- 防火墙(Firewall)代理。
- 同步化(Synchronization)代理。
- 智能引用(Smart Reference)代理。
注意事项:
- 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
- 和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
代码
案例一:出租房屋
- 接口
/**
* @author Luis
* @version 1.0
* @description 出租
* @date 2021/4/24 21:51
*/
public interface Rent {
/**
* 出租房子
*/
void rentHouse();
}
- 房东
/**
* @author Luis
* @version 1.0
* @description 房东: (房东不应该实现Rent接口,要不然房东自己也能出租房屋了,将房屋托管给中介后自己不能在私下出租了!)
* @date 2021/4/24 21:48
*/
public class FangDong {
/**
* 出租房子
*/
public void rentHouse() {
System.out.println("出租房子");
}
}
- 中介
/**
* @author Luis
* @version 1.0
* @description 中介
* @date 2021/4/24 21:49
*/
public class ZhongJie implements Rent {
private FangDong fangDong;
public ZhongJie(FangDong fangDong) {
this.fangDong = fangDong;
}
/**
* 出租房子
*/
@Override
public void rentHouse() {
if (fangDong == null) {
fangDong = new FangDong();
}
fangDong.rentHouse();
}
/**
* 扩展功能:代签合同
*/
protected void sign() {
System.out.println("帮房主和租客待签合同");
}
/**
* 扩展功能:收房租
*/
protected void money() {
System.out.println("帮房主代收房租");
}
}
- 测试
/**
* @author Luis
* @version 1.0
* @description 这种情况的代理模式才算对吧(“完全代理”?)
* @date 2021/4/24 21:44
*/
public class Test {
public static void main(String[] args) {
// 这样调用接口就只能由中介代房主出租房子了
Rent rent = new ZhongJie(new FangDong());
rent.rentHouse();
// 中介还有自己拓展的功能
ZhongJie zhongJie = new ZhongJie(new FangDong());
zhongJie.rentHouse();
zhongJie.money();
zhongJie.sign();
}
}
- 结果
出租房子
出租房子
帮房主代收房租
帮房主和租客待签合同
案例二:加载图片
- Image接口:包含展示图片方法
/**
* @author Luis
* @version 1.0
* @description TODO
* @date 2021/4/24 21:24
*/
public interface Image {
/**
* 展示图片
*/
void display();
}
- 被代理类:包含展示和加载图片方法
/**
* @author Luis
* @version 1.0
* @description 被代理类
* @date 2021/4/24 21:25
*/
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
this.loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
/**
* 从磁盘加载
* @param fileName
*/
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
- 代理类:包含展示和加载图片以及扩展方法
实现展示方法时的优化(无需每次都new RealImage,每次new RealImage都要执行loadFromDisk方法)
/**
* @author Luis
* @version 1.0
* @description 代理类
* @date 2021/3/1 16:29
*/
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
/**
* 还可以扩展被代理类(RealImage),增加新功能
*/
protected void reNameFile() {
System.out.println(fileName + " 重命名为 B.JPG");
}
}
- 测试
public static void main(String[] args) {
Image image = new ProxyImage("A.JPG");
// 图像从磁盘加载
image.display();
// 图像不需要从磁盘加载
image.display();
ProxyImage proxyImage = new ProxyImage("A.JPG");
// 图像从磁盘加载
proxyImage.display();
// 图像不需要从磁盘加载
proxyImage.display();
// 调用扩展新增功能
proxyImage.reNameFile();
}
- 结果
Loading A.JPG
Displaying A.JPG
Displaying A.JPG
Loading A.JPG
Displaying A.JPG
Displaying A.JPG
A.JPG 重命名为 B.JPG