设计模式之代理模式

文章目录

一、介绍

代理模式 (Proxy Pattern),属于结构型设计模式。主要目的是为了解决给对象方法进行增强,又不修改原对象方法。

通过代理类对被代理对象进行代理,可以在被代理对象执行方法前后添加附加功能,丝毫不需要修改原方法中的逻辑。

仅从字面意思我们也可以猜到,在代理模式中,从直接访问对象的方式转变为我们通过代理间接访问对象。这就需要我们引入一个代理类,我们只需要访问代理类即可,由代理类访问目标对象。

为其他对象提供一种代理以控制对这个对象的访问。

二、主要角色

从上面的介绍我们得知,在代理模式中,存在的基本角色有:代理类被代理类抽象接口。下面我们对这几个角色进行阐述:

  • 抽象接口(BarInterface)

    按照面向接口编程思想,我们首先需要创建一个抽象接口来定义其实现类的行为。

  • 被代理类(BarImpl)

    实现抽象接口(BarInterface),对接口中定义的行为进行具体实现。作为被代理类,它的方法实现为该方法的核心逻辑,当其被代理类代理增强后,该实现类方法的逻辑将被增强。

  • 代理类(BarProxy)

    客户端调用的实际类型。在代理模式中,客户端使用的都是被代理类所代理的对象,该对象在原始对象的基础上,对原对象的方法进行增强,添加了一些附加逻辑。

下面是代理模式通用的UML类图

1. 代码演示

根据上面通用UML类图,我们使用代码对其进行实现,通过代码的形式对代理模式有个初步了解。

  • 抽象接口(BarInterface)

    新建接口类BarInterface

    java 复制代码
    public interface BarInterface {
    
        void doSomething_1();
    
        void doSomething_2();
    
        void doSomething_3();
    }
  • 被代理类(BarImpl)

    新建抽象接口类BarInterface的实现类BarImpl,并对接口类所定义的行为进行具体实现。

    java 复制代码
    public class BarImpl implements BarInterface{
    
        @Override
        public void doSomething_1() {
            System.out.println("被代理对象执行方法:doSomething_1()");
        }
    
        @Override
        public void doSomething_2() {
            System.out.println("被代理对象执行方法:doSomething_1()");
        }
    
        @Override
        public void doSomething_3() {
            System.out.println("被代理对象执行方法:doSomething_1()");
        }
    }
  • 代理类(BarProxy)

    新建代理类BarProxy,该类与被代理类BarImpl一样,需要实现抽象接口类BarInterface,并对接口定义的方法进行实现。此时我们发现被代理类BarImpl和代理类BarProxy似乎没什么区别,都实现了相同的接口,因此我们需要从功能上对这两个类进行区别,代理类的功能是对被代理类进行方法增强,因此在代理类中,我们需要通过构造方法初始化一个被代理类对象的字段,然后在代理类的方法内部对被代理进行方法增强即可。具体代码如下所示

    java 复制代码
    public class BarProxy implements BarInterface{
    
        private final BarInterface bar;
        public BarProxy(BarInterface bar) {
            this.bar = bar;
        }
    
        @Override
        public void doSomething_1() {
            System.out.println("被代理对象doSomething_1()方法执行前....");
            bar.doSomething_1();
            System.out.println("被代理对象doSomething_1()方法执行后....");
        }
    
        @Override
        public void doSomething_2() {
            System.out.println("被代理对象doSomething_2()方法执行前....");
            bar.doSomething_2();
            System.out.println("被代理对象doSomething_2()方法执行后....");
        }
    
        @Override
        public void doSomething_3() {
            System.out.println("被代理对象doSomething_3()方法执行前....");
            bar.doSomething_3();
            System.out.println("被代理对象doSomething_3()方法执行后....");
        }
    }
  • 客户端类(ProxyClient)

    新建客户端类ProxyClient,在main()方法中对医生代码进行演示。

    java 复制代码
    public class ProxyClient {
    
        public static void main(String[] args) {
    
            System.out.println("bar对象未被代理时");
            BarInterface bar = new BarImpl();
            bar.doSomething_1();
            bar.doSomething_2();
            bar.doSomething_3();
    
            System.out.println();
            System.out.println("bar对象被代理类BarProxy所代理,方法被代理类增强");
            BarInterface barProxy = new BarProxy(bar);
            barProxy.doSomething_1();
            barProxy.doSomething_2();
            barProxy.doSomething_3();
        }
    }

    执行该方法后,得到以下输出,从输出中,我们发现,将对象进行代理后,其方法被代理类所增强,而且是通过代理类增强,而非是修改原对象中方法的逻辑。

    同时也告诉我们,对象被代理后,其实际类型为该对象对应的代理类型,而不再是原类型。

三、案例

生活中像代理模式的案例到处都是,以租房为例,我们在租房时,往往是直奔租房中介(比如链家、我爱我家)等,中介房源多呀,通过它们给我们找房看房乱七八糟的,我们就可以租到我们心仪的房子了。但这些房子是它们中介的吗?并不是,房子有它自己的主人,只是中介和他们之间有合作,房东平时也挺忙的,没空天天等着我们看房,于是就让中介作为他们的代理人,我们只需要和中介交流就可以了,如果我们愿意租房,中介再和房东之间谈价格。

另外,当我们住进房屋后,如果有电器什么的坏了需要维修,并不是房东直接过来给我们修,房东哪有这个闲工夫,依然是中介作为房东的代理人,过来给我们维修东西。

下面我们就以租房这个事情为例,通过代码进行演示

1. 租房接口类RentHouse

新建租房接口类RentHouse ,我们假设租房分为四个流程:找房、交钱、入住、维修。于是我们在该接口类中定义这些功能。

java 复制代码
public interface RentHouse {
    // 找房子
    void findHouse();
    // 交钱
    void pay();
    // 入住
    void live();
    // 维修
    void maintain();
}

2. 租客类Renter

新建租客类Renter ,并实现**租房接口类RentHouse**中定义的方法。

java 复制代码
public class Renter implements RentHouse{
    @Override
    public void findHouse() {
        System.out.println("租客找房子...");
    }

    @Override
    public void pay() {
        System.out.println("租客支付房屋租金...");
    }

    @Override
    public void live() {
        System.out.println("租客拎包入住...");
    }

    @Override
    public void maintain() {
        System.out.println("维修家电家具...");
    }
}

3. 租房中介代理类RentProxy

新建租房中介代理类RentProxy ,同样实现**租房接口类RentHouse**中定义的方法,中介和租客差不多,都是来租他的房子的,但对于房东来说,中介是租客的代理人,是代表租客来找他租房的,因此在中介代理类RentProxy中需要维护一个租客类作为代理对象。

java 复制代码
public class RentProxy implements RentHouse {

    private final RentHouse rentHouse;

    public RentProxy(RentHouse rentHouse) {
        this.rentHouse = rentHouse;
    }

    @Override
    public void findHouse() {
        System.out.println("中介扩大找房范围");
        rentHouse.findHouse();
    }

    @Override
    public void pay() {
        System.out.println("中介把房屋租金谈下来了,便宜500块");
        rentHouse.pay();
    }

    @Override
    public void live() {
        System.out.println("中介提供免费搬家服务");
        rentHouse.live();
    }

    @Override
    public void maintain() {
        System.out.println("中介找来了维修师傅");
        rentHouse.live();
    }
}

4. 客户端类

新建客户端类RentClient

  • 租客没有通过中介找房

    java 复制代码
    public class RentClient {
    
        public static void main(String[] args) {
            RentHouse rentHouse = new Renter();
            System.out.println("租客直接找房,没有通过中介");
            rentHouse.findHouse();
            rentHouse.pay();
            rentHouse.live();
            rentHouse.maintain();
        }
    }

    此时输出结果如下

  • 租客通过中介找房

    此时我们找房过程的实际类型是中介,代码如下所示

    java 复制代码
    public class RentClient {
    
        public static void main(String[] args) {
            RentHouse rentHouse = new Renter();
    
            System.out.println("租客通过中介找房子");
            System.out.println("---------");
            RentHouse rentProxy = new RentProxy(rentHouse);
            rentProxy.findHouse();
            System.out.println();
    
            rentProxy.pay();
            System.out.println();
    
            rentProxy.live();
            System.out.println();
    
            rentProxy.maintain();
        }
    }

    此时输出结果如下

在此案例中我们可以发现,通过代理模式,我们可以在不改动既有逻辑的情况下,对其进行增强

四、应用

代理模式的应用场景十分广泛,如

  • 日志记录

    我们尝尝在写一个方法时,该方法内仅仅实现其主要逻辑,当我们需要对该方法的调用进行日志记录时,可以通过代理模式对该方法进行增强,在代理类中对该方法进行日志记录。

    如果将日志记录也写在该方法内,则会导致该方法的指责不够清晰且十分臃肿,违背单一职责的设计原则。

  • 权限校验

    有些方法仅允许具有特定权限的用户调用,而没有该权限的用户在调用该方法时要么抛异常。要么直接返回,从而跳过该方法的执行。此时可以将对用户权限的校验交给代理类实现。

    如果将权限校验也写在该方法内,则会导致该方法的指责不够清晰且十分臃肿,违背单一职责的设计原则。

  • 远程代理

    翻墙的原理就是通过对我们的ip进行代理,从而实现科学上网。

  • 等等...

五、优缺点

优点:

  • 对扩展开放,对修改关闭

缺点:

  • 引入代理类,增加系统复杂度

六、与适配器模式的区别

相同点:

  • 两者都引入了一个第三方类(适配器模式引入适配器类,代理模式引入代理类)。且第三方类中都维护了一个被适配对象(或被代理对象)作为内部属性,通过调用适配器的方法(或代理类的方法),在内部对被适配对象(或被代理对象)进行调用。

不同点:

  • 适配器模式的目的是通过适配器类,将A接口转换为B接口;而代理模式中不转换接口,只对接口中的方法进行增强。

七、与装饰器模式的区别

相同点:

  • 两者都引入了一个第三方类(装饰器模式引入装饰器类,代理模式引入代理类)。且第三方类中都维护了一个被装饰对象(或被代理对象)作为内部属性,通过调用装饰器的方法(或代理类的方法),在内部对被装饰对象(或被代理对象)进行调用。
  • 从简化代码来说,装饰器模式和代理模式都是对已有对象进行方法增强。

不同点:

  • 装饰器增强的功能常常深刻影响着被装饰对象,即被装饰的对象会面临被修改的可能;而代理模式中增强的功能与被代理对象没有任何关系,即增强的功能不会对被代理对象产生任何影响。
  • 装饰器模式中,多个装饰器可以通过任何组合方式对被装饰对象进行嵌套装饰,这是一种动态的增强行为;而代理模式中,代理类对被代理对象的增强是由代码设计决定的,一旦在设计阶段对某一对象进行代理,那么在整个程序运行期间不可改变,这是一种静态的增强行为。
  • 装饰器模式中,装饰器可以仅针对被装饰对象的某一个方法进行增强;而代理模式中,代理类针对被代理对象的所有方法进行增强。

纸上得来终觉浅,绝知此事要躬行。

------------------------我是万万岁,我们下期再见------------------------

相关推荐
哪 吒7 小时前
最简单的设计模式,抽象工厂模式,是否属于过度设计?
设计模式·抽象工厂模式
Theodore_10227 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
OkeyProxy7 小时前
什麼是ISP提供的公共IP地址?
代理模式·proxy模式·ip地址·isp·海外ip代理
转世成为计算机大神10 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
小乖兽技术11 小时前
23种设计模式速记法
设计模式
小白不太白95012 小时前
设计模式之 外观模式
microsoft·设计模式·外观模式
小白不太白95012 小时前
设计模式之 原型模式
设计模式·原型模式
澄澈i12 小时前
设计模式学习[8]---原型模式
学习·设计模式·原型模式
kikyo哎哟喂17 小时前
Java 代理模式详解
java·开发语言·代理模式
小白不太白95019 小时前
设计模式之建造者模式
java·设计模式·建造者模式