菜鸟教程 -- 学的不仅是技术,更是梦想!

设计模式
(追記) (追記ここまで)

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能,这种类型的设计模式属于结构型模式。

代理模式通过引入一个代理对象来控制对原对象的访问。代理对象在客户端和目标对象之间充当中介,负责将客户端的请求转发给目标对象,同时可以在转发请求前后进行额外的处理。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

介绍

意图

为其他对象提供一种代理以控制对这个对象的访问。

主要解决的问题

  • 代理模式解决的是在直接访问某些对象时可能遇到的问题,例如对象创建成本高、需要安全控制或远程访问等。

使用场景

  • 当需要在访问一个对象时进行一些控制或额外处理时。

实现方式

  • 增加中间层:创建一个代理类,作为真实对象的中间层。
  • 代理与真实对象组合:代理类持有真实对象的引用,并在访问时进行控制。

关键代码

  • 代理类:实现与真实对象相同的接口,并添加额外的控制逻辑。
  • 真实对象:实际执行任务的对象。

应用实例

  • 快捷方式:Windows系统中的快捷方式作为文件或程序的代理。
  • 角色扮演:孙悟空作为高翠兰的代理,猪八戒无法区分。
  • 代售点:购买火车票时,代售点作为火车站的代理。
  • 支票:作为银行账户资金的代理,控制资金的访问。
  • Spring AOP:使用代理模式来实现面向切面编程。

优点

  • 职责分离:代理模式将访问控制与业务逻辑分离。
  • 扩展性:可以灵活地添加额外的功能或控制。
  • 智能化:可以智能地处理访问请求,如延迟加载、缓存等。

缺点

  • 性能开销:增加了代理层可能会影响请求的处理速度。
  • 实现复杂性:某些类型的代理模式实现起来可能较为复杂。

使用建议

  • 根据具体需求选择合适的代理类型,如远程代理、虚拟代理、保护代理等。
  • 确保代理类与真实对象接口一致,以便客户端透明地使用代理。

注意事项

  • 与适配器模式的区别:适配器模式改变接口,而代理模式不改变接口。
  • 与装饰器模式的区别:装饰器模式用于增强功能,代理模式用于控制访问。

结构

主要涉及到以下几个核心角色:

  • 抽象主题(Subject):

    • 定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
  • 真实主题(Real Subject):

    • 实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。
  • 代理(Proxy):

    • 实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等。
  • 客户端(Client):

    • 使用抽象主题接口来操作真实主题或代理主题,不需要知道具体是哪一个实现类。

实现

我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。

ProxyPatternDemo 类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。

代理模式的 UML 图

步骤 1

创建一个接口。

Image.java

publicinterfaceImage{voiddisplay(); }

步骤 2

创建实现接口的实体类。

RealImage.java

publicclassRealImageimplementsImage{privateStringfileName; publicRealImage(StringfileName){this.fileName = fileName; loadFromDisk(fileName); } @Overridepublicvoiddisplay(){System.out.println("Displaying " + fileName); }privatevoidloadFromDisk(StringfileName){System.out.println("Loading " + fileName); }}

ProxyImage.java

publicclassProxyImageimplementsImage{privateRealImagerealImage; privateStringfileName; publicProxyImage(StringfileName){this.fileName = fileName; } @Overridepublicvoiddisplay(){if(realImage == null){realImage = newRealImage(fileName); }realImage.display(); }}

步骤 3

当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。

ProxyPatternDemo.java

publicclassProxyPatternDemo{publicstaticvoidmain(String[]args){Imageimage = newProxyImage("test_10mb.jpg"); // 图像将从磁盘加载image.display(); System.out.println(""); // 图像不需要从磁盘加载image.display(); }}

步骤 4

执行程序,输出结果:

Loading test_10mb.jpg
Displaying test_10mb.jpg
Displaying test_10mb.jpg
AI 思考中...

5 篇笔记 写笔记

  1. #0

    JDK 自带的动态代理

    • java.lang.reflect.Proxy:生成动态代理类和对象;
    • java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现

    对真实角色的代理访问。

    每次通过 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();
     }
    }
    8年前 (2018年11月13日)
  2. #0

    逆光影者

    248***[email protected]

    30

    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();
     }
    }

    运行结果:

    ### before invocation
    工程师正在吃饭
    ### end invocation

    更多内容可以参考:CGLIB(Code Generation Library) 介绍与原理

    逆光影者

    248***[email protected]

    8年前 (2018年11月29日)
  3. #0

    Siskin.xu

    sis***@sohu.com

    4

    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

    6年前 (2020年03月02日)
  4. #0

    chpeagle

    chp***[email protected]

    7

    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***[email protected]

    6年前 (2020年08月17日)
  5. #0

    代理模式应用:

    1.远程代理:为一个对象在不同的地址空间提供局部代表,这样就可以隐藏一个对象存在于不同地址空间的事实。

    2.虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,这样就可以达到性能的最优化。(例:当我们打开一个很大的HTML文件时,里面可能有很多文字和图片,但是你还是可以很快的打开它,此时你所看到的是所有的文字,但图片却是一张一张的下载后才能看到,那些未打开的图片框,就是通过虚拟代理来替代了真实的图片。)(浏览器就是采用代理模式来这样优化下载的)

    3.安全代理:用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候。

    4.智能指引:是指当调用真实的对象时,代理处理另一些事。(例如计算真实对象的引用次数,这样对该对象没有引用时可以自动释放它;当第一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前检查是否已经锁定它,以确保其他的对象不能改变它。他们都是通过代理在访问一个对象时附加一些内务处理。)

    引用自CSDN 是小光a~ 《代理模式》

    6年前 (2020年08月24日)

点我分享笔记

  • 昵称 (必填)
  • 邮箱 (必填)
  • 引用地址

AltStyle によって変換されたページ (->オリジナル) /