首页 > 其他分享 >初识设计模式 - 代理模式

初识设计模式 - 代理模式

时间:2022-09-26 08:56:33浏览次数:51  
标签:java 对象 代理 接口 display 初识 设计模式 public

简介

概念

举个简单的例说明代理模式就是:假如现在需要买一辆二手车,可以自己去找车源、做质量检测等一系列车辆过户的流程,但是这实在太浪费时间和精力了,其实可以通过找中介的方式,同样会找车源、做质量检测等一系列车辆过户的流程,但是这样自己就只需要选车、付钱即可。

分类

在实际开发中,代理模式根据其目的和实现方式的不同可分为很多种类,如下是常用的几种代理模式:

  • 远程代理:为一个位于不同地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中
  • 虚拟代理:如果需要创建一个资源消耗较大的对象,可以先创建一个消耗相对较小的对象来表示,真实对象只有需要时才会被真正创建
  • 保护代理:控制对一个对象的访问,可以给不同用户提供不同级别的使用权限
  • 缓冲代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
  • 智能引用代理:当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等

功能分类

远程代理

远程代理是一种常见的代理模式,远程代理对象承担了大部分的网络通信工作,使得客户端程序可以访问在远程主机上的对象。

对于客户端而言,无需关心实现具体业务的是谁,只需要按照服务接口所定义的方法直接与本地主机中的代理对象交互即可。

在 Java 语言中,可以通过 RMI(Remote Method Invocation, 远程方法调用) 机制来实现远程代理。代码示例如下:

服务端部分代理

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;

// 远程接口
public interface IMyRemote extends Remote {
    String SayHello() throws RemoteException;
}

// 远程接口实现 - 远程对象
public class MyRemoteImpl extends UnicastRemoteObject implements IMyRemote {
    public MyRemoteImpl() throws RemoteException {
        super();
    }

    @Override
    public String SayHello() throws RemoteException {
        return "Server says, 'Hey'";
    }

    public static void main(String[] args) throws RemoteException, MalformedURLException {
        IMyRemote service = new MyRemoteImpl();
        // 启动本地 RMI 服务,默认端口是 1099
        LocateRegistry.createRegistry(1099);
        // 注册远程对象
        Naming.rebind("rmi://localhost:1099/RemoteHello", service);
    }
}

客户端部分代理

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class MyRemoteClient {
    private void go() throws RemoteException, NotBoundException, MalformedURLException {
        IMyRemote service = (IMyRemote) Naming.lookup("rmi://localhost:1099/RemoteHello");
        System.out.println(service.sayHello());
    }

    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
        new MyRemoteClients().go();
    }
}

虚拟代理

虚拟代理会在真实对象创建成功之前扮演其替身,而当真实对象创建成功之后,虚拟代理再将用户的请求转发给真实对象。

使用虚拟代理的场景非常容易理解,主要有以下两种:

  • 由于对象本身的复杂性或者网络等原因导致一个对象需要较长的加载时间,此时可以用一个加载时间相对较短的代理对象来代表真实对象,通常在实现时结合多线程使用
  • 当一个对象的加载十分耗费系统资源的时候,也非常适合使用虚拟代理

具体实现

代理模式的具体实现从运行时的角度可以分成两种:一种是静态代理,即在代码运行之前就已经确定好代理关系;另一种是动态代理,可以在代码运行时才决定如何实现代理关系。

静态代理

静态代理是比较好理解的实现方式,在这种实现方式中,代理类所实现的接口和所代理的方法都被固定,需要在编译期就预先对原始类编写代理类。

基于接口

一般情况下,参考基于接口而非实现编程的设计思想,为了让代码的改动尽量少,代理类和原始类应该实现同样的接口。

如下是使用图片展示作为例子的代码示例:

图片 Image 接口:描述图片具有的行为

public interface Image {
    void display();
}

展示图片 ShowImage 类:实际的图片原始类

public class ShowImage implements Image {
    public ShowImage() {}

    @Override
    public void display() {
        System.out.println("ShowImage display!");
    }
}

代理图片 ProxyImage 类:在真实图片前包装一层的代理类

public class ProxyImage implements Image {
    // 通过依赖注入的方式
    private ShowImage showImage;

    public ProxyImage(ShowImage showImage) {
        this.showImage = showImage;
    }

    @Override
    public void display() {
        System.out.println("ProxyImage display start!");
        this.showImage.display();
        System.out.println("ProxyImage display end!");
    }
}

基于继承

如果原始类并没有实现接口,并且原始类代码由其他人开发维护,可以通过代理类继承原始类的方法来实现代理模式。

假设上述展示图片 ShowImage 类没有实现接口,可以重新定义代理图片 ProxyImage 类如下:

public class ProxyImage extends ShowImage {
    public ProxyImage() {}

    @Override
    public void display() {
        System.out.println("ProxyImage display start!");
        super.display();
        System.out.println("ProxyImage display end!");
    }
}

动态代理

静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了开发成本和维护成本。

对于静态代理存在的问题,可以通过动态代理来解决。

动态代理的原理是:不事先为每个原始类编写代理类,而是在代码运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

在 Java 中,主要就是利用反射机制在运行时创建代理类。代码示例如下:

// 定义接口,描述行为
public interface Subject {
    void hello(String param);
}

// 实现接口
public class SubjectImpl implements Subject {
    public SubjectImpl() {}

    @Override
    public void hello(String param) {
        System.out.println("hello " + param);
    }
}

// 创建代理类
public class SubjectProxy implements InvocationHandler {
    private Subject subject;

    public SubjectProxy(Subject subject) {
        this.subject = subject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----- Start -----");
        Object invoke = method.invoke(subject, args);
        System.out.println("----- End -----");
        return invoke;
    }
}

// 实际调用
public class Main {
    public static void main(String[] args) {
        Subject subject = new SubjectImpl();
        InvocationHandler subjectProxy = new SubjectProxy(subject);
        // 代理类的类加载器
        // 被代理类的接口,如果有多个就是以数组形式传入
        // 代理类实例
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(
                subjectProxy.getClass().getClassLoader(),
                subject.getClass().getInterfaces(),
                subjectProxy
        );
        // 执行代理方法
        proxyInstance.hello("world");
    }
}

常见的 Java 动态代理实现方式有 JDK 代理、CGLib 代理。基于 JDK 的动态代理必须实现一个接口,而 CGLib 动态代理没有这个限制,是另一种不错的选择。

总结

优点

代理模式的主要优点如下:

  • 协调调用者和被调用者,在一定程度上降低了系统的耦合,满足迪米特原则
  • 客户端可以针对抽象主题角色进行编程,增加和更换代理类无需修改源代码,符合开闭原则
  • 公共的事务由代理来完成,使得真实处理的业务更加纯粹,不再去关注公共业务,而公共业务发生扩展时也变得更加集中和方便

缺点

代理模式的主要缺点如下:

  • 在客户端和真实主题之间增加代理对象,有些类型的代理模式可能会造成请求的处理速度变慢
  • 实现代理模式需要额外的成本,有些代理模式的实现非常复杂

适用场景

代理模式的适用场景如下:

  • 当客户端对象需要访问远程主机中的对象时,可以使用远程代理
  • 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象时,可以使用虚拟代理
  • 当需要控制一个对象的访问,为不同用户提供不同级别的的访问权限时,可以使用保护代理
  • 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时,可以使用缓冲代理
  • 当需要为一个对象的访问(引用)提供一些额外的操作时,可以使用智能引用代理

源码

在 JDK 中,提供了 java.lang.reflect.Proxy 支持创建动态代理类。

标签:java,对象,代理,接口,display,初识,设计模式,public
From: https://www.cnblogs.com/fatedeity/p/16729663.html

相关文章

  • 设计模式---享元模式
    简述类型:结构型目的:降低对象创建时大量属性也随之被新建而带来的性能上的消耗话不多说,我们看一个案例。优化案例最初版v0现在需要采购一批办公用的电脑,以下是Compu......
  • 三层架构以及MVC设计模式
    三层架构三层架构为什么是三层?过去的软件开发种,没有三层构架,只有数据后台和前端显示,这就导致项目很难维护,还有当业务逻辑复杂时,代码量就会多得多。互联网开发的迭代优化......
  • wazuh 代理端安装
    前提上一篇介绍了服务端安装,这篇记录客户端安装生成安装命令进入web界面首先点击addagent根据要安装的服务器系统配置选择复制命令在服务器中安装运行安装命令......
  • 设计模式 -- FactoryMethod(工厂方法)
    工厂方法(FactoryMethod)定义一个用于创建对象的接口,让子类决定实例化哪个类。FactoryMethod使得一个类的实例化延迟(目的:解耦)到子类。在软件系统中,经常会面临着创建对......
  • 初识设计模式 - 原型模式
    简介对于大部分系统来说,创建对象包括申请内存、给成员变量赋值等过程,这些操作耗费的时间基本可以忽略不计。如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算......
  • Steamlit初识和安装入门
    一、前言之前看我同事搭建了一个简易的网页,觉得挺有趣的,一直想学习下来着,然而机缘巧合,前阵子才知道原来是用Streamlit开发的呀,于是又开始了自己的三分钟热情了,哈哈哈......
  • 微服务架构设计模式
    模块化是开发大型、复杂应用程序的基础微服务架构可以消除对某项技术栈的长期依赖微服务架构的弊端和问题:服务的拆分和定义是一项挑战;分布式系统带来的各种复杂性,使开......
  • 简明设计模式---桥接模式
    简述类型:结构型目的:通过抽离出多个维度相互组合(聚合)来代替继承,简化系统。话不多说,看个优化案例。优化案例现有系统中,对于画面窗口的边框有一套样式来控制是否有圆......
  • java设计模式之七大原则
    java设计模式 以下内容为本人的学习笔记,如需要转载,请声明原文链接   https://www.cnblogs.com/lyh1024/p/16724932.html 设计模式1.设计模式的目的编写软件过......
  • 初识html1
    一.概念web1.网站:网页的集合(使用HTML等工具制作)2.网页:构成网站的基本因素(图片、文字、声音、视频、链接)3.HTML:超文本标记语言,HTML不是编程语言    超文本:(1)可以......