代理模式
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。
代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
介绍
意图
为其他对象提供一种代理以控制对这个对象的访问。
主要解决的问题
- 代理模式解决的是在直接访问某些对象时可能遇到的问题,例如对象创建成本高、需要安全控制或远程访问等。
使用场景
- 当需要在访问一个对象时进行一些控制或额外处理时。
实现方式
- 增加中间层:创建一个代理类,作为真实对象的中间层。
- 代理与真实对象组合:代理类持有真实对象的引用,并在访问时进行控制。
关键代码
- 代理类:实现与真实对象相同的接口,并添加额外的控制逻辑。
- 真实对象:实际执行任务的对象。
应用实例
- 快捷方式:Windows系统中的快捷方式作为文件或程序的代理。
- 角色扮演:孙悟空作为高翠兰的代理,猪八戒无法区分。
- 代售点:购买火车票时,代售点作为火车站的代理。
- 支票:作为银行账户资金的代理,控制资金的访问。
- Spring AOP:使用代理模式来实现面向切面编程。
优点
- 职责分离:代理模式将访问控制与业务逻辑分离。
- 扩展性:可以灵活地添加额外的功能或控制。
- 智能化:可以智能地处理访问请求,如延迟加载、缓存等。
缺点
- 性能开销:增加了代理层可能会影响请求的处理速度。
- 实现复杂性:某些类型的代理模式实现起来可能较为复杂。
使用建议
- 根据具体需求选择合适的代理类型,如远程代理、虚拟代理、保护代理等。
- 确保代理类与真实对象接口一致,以便客户端透明地使用代理。
注意事项
- 与适配器模式的区别:适配器模式改变接口,而代理模式不改变接口。
- 与装饰器模式的区别:装饰器模式用于增强功能,代理模式用于控制访问。
结构
主要涉及到以下几个核心角色:
抽象主题(Subject):
- 定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
真实主题(Real Subject):
- 实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。
代理(Proxy):
- 实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等。
客户端(Client):
- 使用抽象主题接口来操作真实主题或代理主题,不需要知道具体是哪一个实现类。
实现
我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。
ProxyPatternDemo 类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。
步骤 1
创建一个接口。
Image.java
public interface Image {
void display();
}
步骤 2
创建实现接口的实体类。
RealImage.java
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
ProxyImage.java
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();
}
}
步骤 3
当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。
ProxyPatternDemo.java
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}
步骤 4
执行程序,输出结果:
Loading test_10mb.jpg Displaying test_10mb.jpg Displaying test_10mb.jpg
gnehcgnaw
949***604@qq.com
参考地址
JDK 自带的动态代理
对真实角色的代理访问。
每次通过 Proxy 生成的代理类对象都要指定对应的处理器对象。
代码:
a) 接口:Subject.java
** * @author gnehcgnaw * @date 2018/11/5 19:29 */ public interface Subject { public int sellBooks(); public String speak(); }b)真实对象:RealSubject.java
/** * @author gnehcgnaw * @date 2018/11/5 18:54 */ public class RealSubject implements Subject{ @Override public int sellBooks() { System.out.println("卖书"); return 1 ; } @Override public String speak() { System.out.println("说话"); return "张三"; } }c)处理器对象:MyInvocationHandler.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 定义一个处理器 * @author gnehcgnaw * @date 2018/11/5 19:26 */ public class MyInvocationHandler implements InvocationHandler { /** * 因为需要处理真实角色,所以要把真实角色传进来 */ Subject realSubject ; public MyInvocationHandler(Subject realSubject) { this.realSubject = realSubject; } /** * * @param proxy 代理类 * @param method 正在调用的方法 * @param args 方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用代理类"); if(method.getName().equals("sellBooks")){ int invoke = (int)method.invoke(realSubject, args); System.out.println("调用的是卖书的方法"); return invoke ; }else { String string = (String) method.invoke(realSubject,args) ; System.out.println("调用的是说话的方法"); return string ; } } }d)调用端:Main.java
import java.lang.reflect.Proxy; /** * 调用类 * @author gnehcgnaw * @date 2018/11/7 20:26 */ public class Client { public static void main(String[] args) { //真实对象 Subject realSubject = new RealSubject(); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject); //代理对象 Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler); proxyClass.sellBooks(); proxyClass.speak(); } }gnehcgnaw
949***604@qq.com
参考地址
逆光影者
248***1388@qq.com
Cglib 动态代理是针对代理的类, 动态生成一个子类, 然后子类覆盖代理类中的方法, 如果是private或是final类修饰的方法,则不会被重写。
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib
需要代理的类:
package cn.cpf.pattern.structure.proxy.cglib; public class Engineer { // 可以被代理 public void eat() { System.out.println("工程师正在吃饭"); } // final 方法不会被生成的字类覆盖 public final void work() { System.out.println("工程师正在工作"); } // private 方法不会被生成的字类覆盖 private void play() { System.out.println("this engineer is playing game"); } }CGLIB 代理类:
package cn.cpf.pattern.structure.proxy.cglib; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { private Object target; public CglibProxy(Object target) { this.target = target; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("### before invocation"); Object result = method.invoke(target, objects); System.out.println("### end invocation"); return result; } public static Object getProxy(Object target) { Enhancer enhancer = new Enhancer(); // 设置需要代理的对象 enhancer.setSuperclass(target.getClass()); // 设置代理人 enhancer.setCallback(new CglibProxy(target)); return enhancer.create(); } }测试方法:
import java.lang.reflect.Method; import java.util.Arrays; public class CglibMainTest { public static void main(String[] args) { // 生成 Cglib 代理类 Engineer engineerProxy = (Engineer) CglibProxy.getProxy(new Engineer()); // 调用相关方法 engineerProxy.eat(); } }运行结果:
逆光影者
248***1388@qq.com
Siskin.xu
sis***@sohu.com
Python 模式:
# Proxy Pattern with Python Code rom abc import abstractmethod,ABCMeta # 定义Image接口 class Image(metaclass=ABCMeta): @abstractmethod def display(self): pass # 实现Image的实体类:RealImage,ProxyImage类 class RealImage(Image): _strFilename = "" def __init__(self,inFilename): self._strFilename = inFilename self.loadFromDisk(inFilename) def display(self): print("Displaying "+self._strFilename) def loadFromDisk(self,inFilename): print("Loadng "+ inFilename) class ProxyImage(Image): _realImage = None _strFilename = "" def __init__(self,inFilename): self._strFilename = inFilename def display(self): if self._realImage is None: self._realImage = RealImage(self._strFilename) self._realImage.display() # 调用输出 if __name__ == '__main__': aImage = ProxyImage("test_10mb.jpg") #图片将从磁盘加载 aImage.display() print("") #图片不需要从磁盘加载 aImage.display()Siskin.xu
sis***@sohu.com
chpeagle
chp***le@126.com
C++ 方式:
Image.h
class Image { public: virtual void display() = 0; }; RealImage.h #include "Image.h" #include <string> #include <iostream> class RealImage : public Image { public: RealImage(std::string& fileName) { this->fileName = fileName; } virtual void display() { std::cout << "Displaying " + fileName<<std::endl; } private: std::string fileName; void loadFromDisk(std::string& fileName) { std::cout << "Loading " + fileName << std::endl; } };ProxyImage.h
#include "Image.h" #include "RealImage.h" class ProxyImage : public Image { public: ProxyImage(std::string &fileName) { this->fileName = fileName; } virtual void display() { if (realImage == nullptr) { realImage = new RealImage(fileName); } realImage->display(); } private: std::string fileName; RealImage *realImage = nullptr; };main.cpp
#include <iostream> #include "ProxyImage.h" int main(int argc, char* argv[]) { std::string fileName = "test_10mb.jpg"; Image* image = new ProxyImage(fileName); image->display(); std::cout << std::endl; image->display(); }chpeagle
chp***le@126.com
九度
297***3065@qq.com
参考地址
代理模式应用:
1.远程代理:为一个对象在不同的地址空间提供局部代表,这样就可以隐藏一个对象存在于不同地址空间的事实。
2.虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,这样就可以达到性能的最优化。(例:当我们打开一个很大的HTML文件时,里面可能有很多文字和图片,但是你还是可以很快的打开它,此时你所看到的是所有的文字,但图片却是一张一张的下载后才能看到,那些未打开的图片框,就是通过虚拟代理来替代了真实的图片。)(浏览器就是采用代理模式来这样优化下载的)
3.安全代理:用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候。
4.智能指引:是指当调用真实的对象时,代理处理另一些事。(例如计算真实对象的引用次数,这样对该对象没有引用时可以自动释放它;当第一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前检查是否已经锁定它,以确保其他的对象不能改变它。他们都是通过代理在访问一个对象时附加一些内务处理。)
引用自CSDN 是小光a~ 《代理模式》
九度
297***3065@qq.com
参考地址