其他分享
首页 > 其他分享> > 23种设计模式——代理模式

23种设计模式——代理模式

作者:互联网

文章目录

23种设计模式——代理模式

1、什么是代理模式

背景:

一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又比如找女朋友、找保姆、找工作等都可以通过找中介完成。

代理模式的定义:

代理模式的目的:

代理模式的作用:

2、代理模式的优缺点

代理模式的主要优点有:

其主要缺点是:

那么如何解决以上提到的缺点呢?答案是可以使用动态代理方式

3、代理模式的结构

代理模式的主要角色如下。

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象(被代理的角色)。
  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
  4. 客户类:访问代理类的人

其结构图如图所示。

代理模式的结构图

在这里我们举一个租房的例子,用来理解代理模式

在这里插入图片描述

在这个例子里面,租房是抽象主题类,房产中介是代理类,房东是真实主题类,租房的人就是客户类,房东就是中介所代表的的真实对象。

4、代理模式的分类

根据代理的创建时期,代理模式分为静态代理和动态代理。

4.1、静态代理

静态代理代码实现租房:

1.接口

//租房
public interface Rent {
    public void rent();
}

2.真实角色

//房东,被代理的真实角色 实现租房Rent这个接口
public class Host implements Rent{

    @Override
    public void rent() {
        System.out.println("房东要把房子租出去");
    }
}

3.代理角色

package com.cheng.demo01;

//中介代理
public class Proxy implements Rent{
    //多用组合,少用继承
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        host.rent();
        seeHost();
        charge();
    }
    //看房,这件事中介能实现,房东不能实现
    public void seeHost(){
        System.out.println("中介带你看房子");
    }
    //收中介费,这件事也只有中介能实现
    public void charge(){
        System.out.println("中介收你中介费");
    }
}

4.客户类

//要租房的人
public class Client {
    public static void main(String[] args) {
        Host host = new Host();//获得房东
        //因为房东不能直接出租房子,所以我们要找中介(代理),通过代理访问房东
        //代理一般会有一些附属操作,例如看房,收中介费
        Proxy proxy = new Proxy(host);
        proxy.rent();//本质还是调用了房东的rent方法


    }
}

静态代理代码实现二:

1.接口,只有增删改查方法

//抽象角色
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

2.真实角色,实现了增删改查方法

package com.cheng.demo02;

import java.util.Random;
//真实角色
public class UserServiceImpl implements UserService{

    @Override
    public void add() {
        System.out.println("增加了一个用户");
    }
    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }
    @Override
    public void update() {
        System.out.println("修改了一个用户");
    }
    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}

要求:如果在不改变原有业务代码的情况下,想要在执行接口中的方法的时候,打印出日志,这时候最好的方法就是用静态代理实现。

3.代理类

package com.cheng.demo02;
//代理类
public class UserServiceProxy implements UserService{

    private UserServiceImpl userService;

    //使用set注入对象
    public void setUserService(UserServiceImpl userService){
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }
    //日志的方法
    public void log(String msg){
        System.out.println("使用了"+msg+"方法");
    }
}

4.客户类

//客户类
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.add();
    }
}

4.2、动态代理

动态代理:在程序运行时,运用反射机制动态创建而成

动态代理分类:

java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口InvocationHandler是我们实现动态代理的核心

4.2.1、InvocationHandler接口

invoke方法详细信息:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
   }

处理代理实例上的方法调用并返回结果。 当在与之关联的代理实例上调用方法时,将在调用处理程序中调用此方法。

参数

4.2.2、Proxy类

Proxy类提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

它提供了很多方法,但是我们最常用的是newProxyInstance方法。

public static Object newProxyInstance(ClassLoader loader, 
                                        Class<?>[] interfaces, 
                                        InvocationHandler h)

这个方法的作用就是创建一个代理类对象,它接收三个参数:

4.2.3、动态代理实现

实现一:租房

接口

//租房
public interface Rent {
    public void rent();
}

真实角色

//房东,被代理的真实角色 实现租房Rent这个接口
public class Host implements Rent {
    public void rent() {
        System.out.println("房东要把房子租出去");
    }
}

由proxy代理实例的调用处理程序

package com.cheng.demo03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//我们将会用这个类自动生成代理类,这个类(ProxyInvocationHandler)只是proxy代理实例的调用处理程序
public class ProxyInvocationHandler implements InvocationHandler {

    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理类
    public Object getProxy(){
      //this.getClass().getClassLoader()代表加载类 rent.getClass().getInterfaces()代表要实现的接口 this代表当前InvocationHandler接口对象
      return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }
    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHose();
        fare();//这两个方法会通过反射动态的加载到代理类中04
        //动态代理的本质还是反射机制
        Object result = method.invoke(rent,args);
        return result;
    }
    public void seeHose(){
        System.out.println("中介带你看房子");
    }
    public void fare(){
        System.out.println("收中介费");
    }
}

客户类

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色,暂时还没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过处理程序来实现接口对象 这里用了多态,相当于 Rent rent = new Host();
        pih.setRent(host);
        //动态生成的代理类
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

测试

在这里插入图片描述

实现二:用动态代理优化4.1静态代理中的代码实现二

接口

//抽象角色
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

由proxy代理实例的调用处理程序,我们可以把它封装成一个工具类

package com.cheng.demo04;

import com.cheng.demo03.Rent;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//我们将会用这个类自动生成代理类,这只是一个处理程序
public class ProxyInvocationHandler implements InvocationHandler {

    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成代理类
    public Object getProxy(){
      //this.getClass().getClassLoader()代表加载类 rent.getClass().getInterfaces()代表要实现的接口 this代表当前InvocationHandler接口对象
      return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }
    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质还是反射机制
        log(method.getName());//用反射动态的获取方法名
        Object result = method.invoke(target,args);
        return result;
    }
    //增加日志功能
    public void log(String msg){
        System.out.println("[Debug]:使用了"+msg+"方法");
    }
}

客户类

public class Client {
    public static void main(String[] args) {
        //获得真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //获得代理角色,暂时没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //处理程序处理接口对象
        pih.setTarget(userService);
        UserService proxy = (UserService) pih.getProxy();
        proxy.add();
    }
}

相比静态代理,动态代理的优点:

例如:在上面的例子中,只要实现了UserService这个接口的类,都可以被动态代理类代理。

5、代理模式的应用场景

当无法或不想直接引用某个对象或访问某个对象存在困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:一是保护目标对象,二是增强目标对象。

前面分析了代理模式的结构与特点,现在来分析以下的应用场景。

标签:23,对象,void,代理,接口,rent,设计模式,public
来源: https://blog.csdn.net/wpc2018/article/details/115581684