• 【设计模式(12)】结构型模式之代理模式


    个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道

    如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充


    前言

    “中介”这个职业大家都知道,比如买房租房,我们一般是与中介进行交流、看房、议价等等,甚至我上一次租的房住了一年都没见过房东,同理房东也没见过我。。。

    还有譬如买火车票并不需要去火车站,在携程或者12306app上就可以购买;订酒店通过美团或者携程也能够完成,并不需要本人去酒店;还有浏览那些“不存在”的网站的时候,我们需要“梯子“等等


    以租房中介来说,一方面他是我的中介,代替我去跟房东谈价格谈房屋配置,谈妥之前房东并不知道我是谁,相当于我的代理。

    另一方面他也是房东的中介,代替房东跟我谈价格谈合同,谈妥之前房东同样不知道我是谁,相当于房东的代理。

    而且在代理的过程中,并不一定会原话转达给对方,大概率会做一些小的变动,以促成合同的完成。这是他的能力范围内的事情,当然会怎么做看个人咯。


    说到底,之所以需要中介,是因为我们不想或者不能直接访问目标对象


    代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。

    这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。


    1.介绍

    使用目的:为其他对象提供一种代理以控制对这个对象的访问

    使用时机:需要在访问一个类时添加一些控制

    解决问题:由于某些原因客户端不能或者不想要直接访问目标对象

    实现方法:添加中间层,用于实现与被代理类组合。客户端访问中间层,以间接访问被代理类

    应用实例

    • Windows系统的快捷方式
    • 机票、车票、酒店等代售点
    • 房屋中介、职务中介等
    • spring中的aop(面向切面编程)

    优点

    • 客户端不能直接访问目标对象,可以保护目标对象,提高安全性
    • 客户端与目标对象解耦,职责清晰,也提高了程序的扩展性
    • 代理可以扩展目标对象的功能

    缺点

    • 代理模式需要在系统中新增类,提高了系统体积
    • 客户端和目标对象中增加了代理对象,会一定程度上降低执行速度
    • 实现代理模式需要额外的工作,有些代理模式的实现非常复杂

    使用场景:按职责来划分,通常有以下使用场景

    说来话长,不多赘述

    • 远程代理
    • 虚拟代理
    • Copy-on-Write 代理
    • 保护(Protect or Access)代理
    • Cache代理
    • 防火墙(Firewall)代理
    • 同步化(Synchronization)代理
    • 智能引用(Smart Reference)代理

    2.结构

    代理模式的主要角色如下

    • 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

    • 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

    • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。image-20201029115801546

    • 代理类和真实主题类都需要实现接口抽象主题类,以保证其对外接口一致

    • 客户端调用代理类中的接口,实际上是对真实主题类中方法加上前置后置处理后的方法,但仍然需要调用真实主题类的方法

    • 代理类需要持有一个真实主题类对象,以调用其中的目标方法


    3.实现步骤

    1. 定义抽象主题接口

      interface Subject {
          void Request();
      }
      
    2. 定义真实主体类,实现抽象主题接口

      class RealSubject implements Subject {
          @Override
          public void Request() {
              System.out.println("访问真实主题方法...");
          }
      }
      
    3. 定义代理类,实现抽象主题接口,并持有一个真实主题对象

      class Proxy implements Subject {
          private RealSubject realSubject;
      
          public Proxy() {
              this.realSubject = new RealSubject();
          }
      
          @Override
          public void Request() {
              preRequest();
              realSubject.Request();
              postRequest();
          }
      
          private void preRequest() {
              System.out.println("访问真实主题之前的预处理。");
          }
      
          private void postRequest() {
              System.out.println("访问真实主题之后的后续处理。");
          }
      }
      

      需要持有一个真实主题对象,构造这个对象的时间可以按照业务处理,一般有下面3种情况

      • 写在代理类构造函数中,构造代理类的同时构造真实主题对象
      • 写在接口中,调用接口时检查是否已构造,没有则此时构造
      • 在客户端中构造真实主题对象,通过代理类的构造函数传入代理类

    完整代码

    package com.company.test.proxy;
    
    //抽象主题
    interface Subject {
        void Request();
    }
    
    //真实主题
    class RealSubject implements Subject {
        @Override
        public void Request() {
            System.out.println("访问真实主题方法...");
        }
    }
    
    //代理
    class Proxy implements Subject {
        private RealSubject realSubject;
    
        public Proxy() {
            this.realSubject = new RealSubject();
        }
    
        @Override
        public void Request() {
            preRequest();
            realSubject.Request();
            postRequest();
        }
    
        private void preRequest() {
            System.out.println("访问真实主题之前的预处理。");
        }
    
        private void postRequest() {
            System.out.println("访问真实主题之后的后续处理。");
        }
    }
    
    public class ProxyTest {
        public static void main(String[] args) {
            Proxy proxy = new Proxy();
            proxy.Request();
        }
    }
    

    运行结果

    image-20201029120959622

    4.正向代理与反向代理

    都是代理,一个正一个反,那么到底是是啥反了?答案是代理的对象反了

    通常说三个角色,客户端—代理—服务端

    • 正向代理是代理客户端,隐藏客户端的真实身份

      如VPN,VPN服务器会代替我们去访问目标网站。对于目标网站而言,交互的对象是代理,他并不知道代理的背后是谁,甚至不知道是不是代理。

      目的正是让服务端不知道是谁在访问。

    • 反向代理是代理服务端,隐藏服务端的真实身份

      如10086或者10010客服热线,我们拨的同一个电话,但是对面的人大概率每次都不一样,但他们都会说自己是客服,实际上我们也不关心是谁,我们只关心能不能解决自己的问题。

      很显然客服不止一个,我们拨过去后系统会选择空闲的客服接线给我们,以保证不会出现长时间排队的情况。

      虽然代理了服务端,隐藏其身份,但这个不是目的。

      目的是让代理去选择合适的服务端,只是没打算告知客户端罢了。对客户端而言,交互对象是代理,并不知道代理的对象是谁

    正向代理代理的对象是客户端,反向代理代理的对象是服务端

    主要是用在服务器层面的,为了避免偏题,此处不再赘述,有兴趣的可以去这两个地方看看,讲得不错

    https://blog.csdn.net/lixiangss1993/article/details/87934562

    https://www.zhihu.com/question/24723688


    5.静态代理与动态代理

    上述步骤就是静态代理,但存在以下缺陷

    代理主题是为真实主题专门配备的,因此是一对一的关系,且使用同一个接口进行规范。

    因此代理主题必须基于已有的真实主题,每新增一个真实主题需要新增一个代理主题。


    动态代理的出现则是为了解决这种缺陷

    动态代理利用反射机制在运行时创建代理类,客户端调用代理来执行服务端方法,因此一个代理类适用于所有服务端。

    package com.company.test.invocation;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    //抽象主题
    interface Subject {
        void Request(String s);
    }
    
    //真实主题
    class RealSubject implements Subject {
        @Override
        public void Request(String s) {
            System.out.println("访问真实主题方法...");
        }
    }
    
    //代理
    class ProxyHandler implements InvocationHandler {
        private Object object;
    
        public ProxyHandler(Object object) {
            this.object = object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before invoke " + method.getName());
            method.invoke(object, args);
            System.out.println("After invoke " + method.getName());
            return null;
        }
    }
    
    public class InvocationTest {
        public static void main(String[] args) {
            Subject subject = new RealSubject();
            Subject realSubject = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyHandler(subject));
            realSubject.Request("hello?");
        }
    }
    

    image-20201029155739485

    通过这样的处理方式,我们将subject托管给ProxyHandler代理。

    我们可以在invoke方法中进行前置处理,后置处理,甚至修改参数(args),或者直接抛出异常

    实际使用的过程中,我们通常使用AOP(面向切面编程)以注解的方式进行代理,有兴趣的可以看看之前我整理的JWT的自定义切面拦截器

    https://blog.csdn.net/qq_25978501/article/details/108409031


    后记

    代理模式真要学完可能得几天时间,有空再整理细说吧,先做了解,实战中会遇到不少的,特别是AOP几乎算是面试必问的了


    作者:Echo_Ye

    WX:Echo_YeZ

    Email :echo_yezi@qq.com

    个人站点:在搭了在搭了。。。(右键 - 新建文件夹)

  • 相关阅读:
    用tensorflow构建神经网络学习简单函数
    Entity Framework 6.0 Tutorials(1):Introduction
    Entity Framework Tutorial Basics(43):Download Sample Project
    Entity Framework Tutorial Basics(42):Colored Entity
    Entity Framework Tutorial Basics(41):Multiple Diagrams
    Entity Framework Tutorial Basics(40):Validate Entity
    Entity Framework Tutorial Basics(39):Raw SQL Query
    Entity Framework Tutorial Basics(38):Explicit Loading
    Entity Framework Tutorial Basics(37):Lazy Loading
    Entity Framework Tutorial Basics(36):Eager Loading
  • 原文地址:https://www.cnblogs.com/silent-bug/p/13897377.html
Copyright © 2020-2023  润新知