聊聊设计模式之代理模式

简介: 前言代理模式的目的是提供一个代理来控制对一个对象的访问。那么,我们为什么需要控制对一个对象的访问呢?或者说控制对一个对象的访问有什么好处呢?在日常工作中,大家应该会遇到过以下这些问题:一个对象的加载时间太长或者太耗费资...

前言

代理模式的目的是提供一个代理来控制对一个对象的访问。那么,我们为什么需要控制对一个对象的访问呢?或者说控制对一个对象的访问有什么好处呢?在日常工作中,大家应该会遇到过以下这些问题:一个对象的加载时间太长或者太耗费资源,因此我们需要在必要的时候再加载它;一个对象运行在其他计算机上,而我们需要调用该对象的某些方法;我们可能还需要拦截一个对象的某些方法,并在该方法前后加入一些业务逻辑……以上问题就非常适合用代理模式来解决,因为我们可以通过代理对象控制对真实对象的访问,这样可以做到在必要的时候才加载真实对象或者在真实对象的方法上加入一些业务逻辑。

背景

假如我们有一个用户登录的LoginService接口,其有一个login方法,用来实现用户登录功能。LoginService接口有一个实现类:LoginServiceImpl,其实现了login方法,提供了基本的用户登录功能。在系统稳定运行了一段时间之后,我们发现有一些用户经常发一些违规内容,因此我们需要限制此类用户的登录。最简单的方法就是在LoginServiceImpl的login方法里面修改原先的代码,加入对违规用户登录的检查,但是这样做的问题是容易出现bug,假如说原先的login方法已经很复杂了,这个时候即使只修改几行代码都可以会出现问题。再者,软件开发的一个基本原则是“对扩展开放,对修改关闭”,也就是说我们应该尽可能地通过扩展来适应功能的变化,而不是通过修改已有的代码来满足新需求。所以,在原来的LoginServiceImpl上对login方法进行修改的方案虽然简单,却有诸多缺点,因此我们接下来介绍通过代理模式来实现对原来的登录功能进行扩展。

代理模式的结构

首先我们先讲一下代理模式的类图结构。

  • RealSubject是真实对象

  • Proxy是代理对象,其持有真实对象的引用

  • Subject是真实对象跟代理对象的共有接口

由于代理对象与真实对象都实现了相同的接口,所以可以在一定程度上“代替”真实对象,充当真实对象的“代理”。又因为代理对象持有真实对象的引用,所以可以在必要的时候对真实对象进行调用。

使用代理模式

接下来,我们用代理模式来实现对上述用户登录功能的扩展。

我们首先编写LoginService接口:

public interface LoginService {    
    void login(String userId);
}

其只有一个抽象方法login。

接着我们编写LoginServiceImpl类以实现最基本的登录功能:

public class LoginServiceImpl implements LoginService{

    @Override
    public void login(String userId) {        
        System.out.println("登录成功");
    }
}

在LoginServiceImpl类,我们默认让所有用户都能正常登录。

然后我们编写代理类,以限制违规用户的登录:

public class LoginServiceProxy implements LoginService{

    private LoginService realLoginService;    

    public LoginServiceProxy(LoginService realLoginService) {
        this.realLoginService = realLoginService;
    }    

    @Override
    public void login(String userId) {        
        if(isIllegalUser(userId)){
            System.out.println("你的账号涉嫌违规,禁止登录。");  
            return;
        }
        realLoginService.login(userId);
    }    

     private boolean isIllegalUser(String userId){
        if(userId.equalsIgnoreCase("user1")){
            return true;
        }        
        return false;
    }

}

代理类LoginServiceProxy同样也是实现了LoginService接口,并持有真实对象的引用。在代理类的login方法中,我们对用户是否违规进行了判断,如果用户是违规用户,则禁止登录,否则就调用真实对象的login方法走正常的登录流程。

最后,我们写一个客户端测试一下:

public class Client {
    public static void main(String[] args) {
        LoginService realLoginService=new LoginServiceImpl();
        LoginService loginServiceProxy=new LoginServiceProxy(realLoginService);
        loginServiceProxy.login("user2");
        loginServiceProxy.login("user1");
    }
}

结果输出如下:

可以看到,我们已经在原来的登录功能之上增加了对违规用户的过滤功能。不仅如此,我们并没有改动原来的LoginServiceImpl类中的一行代码,假如有一天我们不需要违规用户的过滤功能了,则客户端直接使用LoginServiceImpl类的登录功能即可,不需要做其他改动。

代理模式的改进

上述代理模式通常称为静态代理,其有一个弊端就是,如果LoginService发生变化,比如增加了新的方法,那么所有目标类跟代理类都需要做变动,不是很灵活。实际上在Java中还有另外一种代理模式,称为动态代理。其优点是无论目标接口定义了多少方法,动态代理类始终只有一个invoke方法。这样一来,当目标接口变化的时候,动态代理类就不需要根据接口的变化而变化了。

如果觉得这篇文章对你有帮助,可以扫描下方二维码,关注本人公众号,获得更多优质文章推送。

相关文章
|
6月前
|
设计模式 缓存 监控
【设计模式系列笔记】代理模式
代理模式是一种结构型设计模式,它允许一个对象(代理对象)控制另一个对象的访问。代理对象通常充当客户端和实际对象之间的中介,用于对实际对象的访问进行控制、监控或其他目的。
114 1
|
6月前
|
设计模式 缓存 安全
小谈设计模式(8)—代理模式
小谈设计模式(8)—代理模式
|
2月前
|
设计模式 缓存 安全
设计模式——代理模式
静态代理、JDK动态代理、Cglib 代理
设计模式——代理模式
|
6月前
|
设计模式 Java
Java一分钟之-设计模式:装饰器模式与代理模式
【5月更文挑战第17天】本文探讨了装饰器模式和代理模式,两者都是在不改变原有对象基础上添加新功能。装饰器模式用于动态扩展对象功能,但过度使用可能导致类数量过多;代理模式用于控制对象访问,可能引入额外性能开销。文中通过 Java 代码示例展示了两种模式的实现。理解并恰当运用这些模式能提升代码的可扩展性和可维护性。
63 1
|
2月前
|
设计模式 Java 数据安全/隐私保护
Java设计模式-代理模式(7)
Java设计模式-代理模式(7)
|
6月前
|
设计模式 Java 数据库连接
【重温设计模式】代理模式及其Java示例
【重温设计模式】代理模式及其Java示例
|
3月前
|
设计模式 缓存 Java
【十一】设计模式~~~结构型模式~~~代理模式(Java)
文章详细介绍了代理模式(Proxy Pattern),这是一种对象结构型模式,用于给对象提供一个代理以控制对它的访问。文中阐述了代理模式的动机、定义、结构、优点、缺点和适用环境,并探讨了远程代理、虚拟代理、保护代理等不同代理形式。通过一个商务信息查询系统的实例,展示了如何使用代理模式来增加身份验证和日志记录功能,同时保持客户端代码的无差别对待。此外,还讨论了代理模式在分布式技术和Spring AOP中的应用,以及动态代理的概念。
【十一】设计模式~~~结构型模式~~~代理模式(Java)
|
3月前
|
设计模式
设计模式的基础问题之代理模式在工作中的问题如何解决
设计模式的基础问题之代理模式在工作中的问题如何解决
|
4月前
|
设计模式 算法 Go
iLogtail设计模式问题之代理模式在iLogtail中是如何应用的
iLogtail设计模式问题之代理模式在iLogtail中是如何应用的
|
4月前
|
设计模式 缓存 JavaScript
js设计模式【详解】—— 代理模式
js设计模式【详解】—— 代理模式
31 0