【设计模式】代理模式

本文详细介绍了Java中的代理模式,包括其定义、角色、分类(静态代理、JDK动态代理、Cglib动态代理),并给出实战案例。同时分析了代理模式的优缺点,指出它能使代码结构清晰、增强功能,但也会增加系统复杂度和开发难度。


在这里插入图片描述

主页传送门:💁 传送

1.代理模式定义

       代理模糊模式(Proxy Pattern)是一种使用率非常高的设计模式。其定义如下:

Provide a surrogate or placeholder for another object to control access to it.

       即:为其它对象提供一种代理以控制对这个对象的访问。
       代理模式的通用类图如下:

+RealSubject
Subject
+Construct()
RealSubject
+BuilderPart()
Proxy

2.代理模式的角色

在代理模式中的角色:

  • 抽象对象角色
    声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
  • 目标对象角色
    定义了代理对象所代表的目标对象。
  • 代理对象角色
    代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。

3.代理模式分类

3.1.静态代理

定义:

静态代理是一种在代码编写期进行代理类和被代理类的关联的代理方式。具体实现是创建一个代理类,通常需要实现与被代理类相同的接口或继承被代理类。

适用场景:

  • 当代理对象只有一个时,可以使用静态代理
  • 当被代理的类的接口比较稳定时,可以使用静态代理
  • 当需要为多个被代理的类提供代理时,会导致代理类过多,不方便管理和维护,所以不建议使用静态代理。

3.2.JDK动态代理

定义:

JDK动态代理是一种比较常见的代理方式,它是在程序运行时动态生成代理类,也就是说我们在编写代码时并不知道具体代理的是什么类,而是在程序运行时动态生成。

适用场景:

  • 对象必须实现一个或多个接口
  • 代理类的代理方法不需要额外的逻辑

3.3.Cglib动态代理

定义:

CGLIB代理是在运行时动态生成代理类的方式,它使用的库是cglib,和JDK代理相比,它不是动态的生成一个实现了接口的代理类,而是直接在内存中构建一个被代理类的子类,并重写父类的方法来进行代理。

适用场景:

  • 被代理的类没有实现接口或者无法实现接口
  • 代理类的代理方法需要进行额外的逻辑,如事务处理等。

4.代理模式实战案例

4.1.静态代理

       将车站的售票服务抽象出一个接口TicketService,包含问询,卖票,退票功能,车站类Station实现了TicketService接口,车票代售点StationProxy则实现了代理角色的功能,类图如下所示。

+Station
«interface»
TicketService
+void inquire()
+void sellTicket()
+void withdraw()
Station
+void inquire()
+void sellTicket()
+void withdraw()
StationProxy
+void inquire()
+void sellTicket()
+void withdraw()

代码如下:
售票服务

public interface TicketService {  
    //售票  
    public void sellTicket();  
    //问询  
    public void inquire();  
    //退票  
    public void withdraw();  
}

车站售票

public class Station implements TicketService {  

    @Override  
    public void sellTicket() {  
        System.out.println("\n\t售票.....\n");  
    }  

    @Override  
    public void inquire() {  
        System.out.println("\n\t问询。。。。\n");  
    }  

    @Override  
    public void withdraw() {  
        System.out.println("\n\t退票......\n");  
    }  

}

代售点服务

public class StationProxy implements TicketService {  

    private Station station;  

    public StationProxy(Station station){  
        this.station = station;  
    }  

    @Override  
    public void sellTicket() {  

        // 1.做真正业务前,提示信息  
        this.showAlertInfo("××××您正在使用车票代售点进行购票,每张票将会收取5元手续费!××××");  
        // 2.调用真实业务逻辑  
        station.sellTicket();  
        // 3.后处理  
        this.takeHandlingFee();  
        this.showAlertInfo("××××欢迎您的光临,再见!××××\n");  

    }  

    @Override  
    public void inquire() {  
        // 1.做真正业务前,提示信息  
        this.showAlertInfo("××××欢迎光临本代售点,问询服务不会收取任何费用,本问询信息仅供参考,具体信息以车站真实数据为准!××××");  
        // 2.调用真实逻辑  
        station.inquire();  
        // 3。后处理  
        this.showAlertInfo("××××欢迎您的光临,再见!××××\n");  
    }  

    @Override  
    public void withdraw() {  
        // 1.真正业务前处理  
        this.showAlertInfo("××××欢迎光临本代售点,退票除了扣除票额的20%外,本代理处额外加收2元手续费!××××");  
        // 2.调用真正业务逻辑  
        station.withdraw();  
        // 3.后处理  
        this.takeHandlingFee();  

    }  

    /* 
     * 展示额外信息 
     */  
    private void showAlertInfo(String info) {  
        System.out.println(info);  
    }  

    /* 
     * 收取手续费 
     */  
    private void takeHandlingFee() {  
        System.out.println("收取手续费,打印发票。。。。。\n");  
    }  

}

4.2.动态代理

       给一个项目中service层的实现类中所有函数都添加一个log打印功能,这里以UserServiceImpl为例。类图如下所示:

«interface»
IUserService
+void add()
+void delete()
+void update()
+void query()
UserServiceImpl
+void add()
+void delete()
+void update()
+void query()
«interface»
InvocationHandler
+Object invoke()
ProxyInvocationHandler
+Object invoke()
Client

代码如下:
IUserService接口

public interface IUserService {
    void add();

    void delete();

    void update();

    void query();
}

UserServiceImpl实现

public class UserServiceImpl implements IUserService {
    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void query() {
        System.out.println("query");
    }
}

代理类生成类

public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;

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

    public Object getProxy() {
        return Proxy.newProxyInstance(getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    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 s) {
        System.out.println("[debug]:" + s);
    }
}

客户端测试类

public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        handler.setTarget(userService);
        IUserService proxy = (IUserService) handler.getProxy();
        proxy.add();
        proxy.delete();
    }
}

5.代理模式优缺点

代理模式的优点包括:

  • 能够使代码结构更加清晰,将实现类和代理类分离,降低了业务实现类的复杂度。
  • 可以隐藏真实的实现细节,用户不需要去了解真实实现,减少了耦合度。
  • 可以方便地实现对目标对象的功能增强,可以在代理对象中增加一些额外的操作,比如权限认证、缓存、日志记录等。
  • 可以实现远程访问,通过代理对象可以在不同的地址空间中进行通信,在客户端和服务器端之间建立一个中间层,可以降低网络带宽和延迟。

然而,代理模式也存在一些缺点:

  • 增加了系统的复杂度,因为需要增加一个代理类来实现额外的功能,所以程序会有更多的类。
  • 代理模式会使系统运行速度变慢,因为代理对象需要额外的处理,所以会增加程序的运行时间和消耗的资源。
  • 代理模式会增加系统开发的难度,因为需要设计和开发额外的代理类。如果系统中存在大量的类,那么代理模式的开发和维护成本会很高。

6.代理模式总结

     代理模式是一种结构型设计模式,它定义了一个代理对象来控制和管理访问另一个对象。代理模式的主要作用是在不改变原有代码的情况下,增强被代理对象的功能,并提供额外的控制和管理。
     代理模式包括三种类型:静态代理、动态代理和虚拟代理。静态代理是指代理类在编译时就已经确定的代理方式,动态代理是指代理类在运行时根据需要动态生成的代理方式,而虚拟代理则是指代理类可以控制对真实对象的访问方式,以达到延迟加载的效果。

如果喜欢的话,欢迎 🤞关注 👍点赞 💬评论 🤝收藏 🙌一起讨论
你的支持就是我✍️创作的动力! 💞💞💞

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农桶子哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值