代理模式

128 阅读5分钟

代理模式

本篇装饰模式,借鉴参考《大话设计模式》中的例子,使用了 Java 来进行简单的实现。

小明是小红隔壁班的,小明通过小红的同班同学小王来给小红送礼物。

mermaid 的类图在手机上显示不友好,推荐使用电脑进行阅读。也可以克隆项目到本地,在 idea 中安装 mermaid 插件进行阅读。

项目地址:design-patterns ,将会持续更新各种模式的应用,以及后续的实际案例应用。

简单实现

没有代理的实现

追求者类

public class Pursuer {

    Pursued mm;

    public Pursuer(Pursued mm) {
        this.mm = mm;
    }

    public void giveDoll() {
        System.out.println(mm.getName() + " 送你洋娃娃");
    }

    public void giveFlowers() {
        System.out.println(mm.getName() + " 送你鲜花");
    }

    public void giveChocolate() {
        System.out.println(mm.getName() + " 送你巧克力");
    }
}

被追求者类

public class Pursued {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

客户端

public class Client {

    public static void main(String[] args) {
        Pursued xh = new Pursued();
        xh.setName("小红");

        Pursuer xm = new Pursuer(xh);
        xm.giveChocolate();
        xm.giveDoll();
        xm.giveFlowers();
        System.out.println();
    }
}

观察一下代码,Pursuer xm = new Pursuer(xh); 这些写的话就等于小明亲自送礼物给小红了,不符合我们的题目要求。

只有代理的实现

代理类

public class Proxy {

    Pursued mm;

    public Proxy(Pursued mm) {
        this.mm = mm;
    }

    public void giveDoll() {
        System.out.println(mm.getName() + " 送你洋娃娃");
    }

    public void giveFlowers() {
        System.out.println(mm.getName() + " 送你鲜花");
    }

    public void giveChocolate() {
        System.out.println(mm.getName() + " 送你巧克力");
    }
}

被追求者类

public class Pursued {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

客户端

public class Client {

    public static void main(String[] args) {
        Pursued xh = new Pursued();
        xh.setName("小红");

        Proxy xw = new Proxy(xh);
        xw.giveChocolate();
        xw.giveDoll();
        xw.giveFlowers();
        System.out.println();
    }
}

观察上面的代码,只有代理的实现中没有了小明的概念,相当于小王直接把礼物送给了小红,但礼物其实是小明 买的。由此可以想到,代码的逻辑应该为 Pursuer(追求者) 通过 Proxy(代理者) 送给 Pursued(被追求者) 礼物,这才应该是合理的,可是应该怎么做呢?

再仔细观察一下,Pursuer(追求者)Proxy(代理者) 其实都用送礼物的行为,只不过 Proxy(代理者) 送的礼物是 Pursuer(追求者) 买的,实质上应该是 Pursuer(追求者) 送的礼物。那既然两者都有相同的方法,那就意味着他们都可以实现相同的接口了。

改造代理模式

代理接口

public interface IGiveGift {

    /**
     * 送洋娃娃
     */
    void giveDoll();

    /**
     * 送花
     */
    void giveFlowers();

    /**
     * 送巧克力
     */
    void giveChocolate();
}

追求者

public class Pursuer implements IGiveGift {

    Pursued mm;

    public Pursuer(Pursued pursued) {
        mm = pursued;
    }

    @Override
    public void giveDoll() {
        System.out.println(mm.getName() + " 送你洋娃娃");
    }

    @Override
    public void giveFlowers() {
        System.out.println(mm.getName() + " 送你鲜花");
    }

    @Override
    public void giveChocolate() {
        System.out.println(mm.getName() + " 送你巧克力");
    }
}

被追求者

public class Pursued {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

代理类

public class Proxy implements IGiveGift {

    Pursuer gg;

    public Proxy(Pursued mm) {
        gg = new Pursuer(mm);
    }

    @Override
    public void giveDoll() {
        gg.giveDoll();
    }

    @Override
    public void giveFlowers() {
        gg.giveFlowers();
    }

    @Override
    public void giveChocolate() {
        gg.giveChocolate();
    }
}

客户端

public class Client {

    public static void main(String[] args) {
        Pursued xh = new Pursued();
        xh.setName("小红");

        Proxy proxy = new Proxy(xh);
        proxy.giveChocolate();
        proxy.giveDoll();
        proxy.giveFlowers();
        System.out.println();
    }
}

在客户端代码中可以看到,小红收到的礼物其实是小明通过代理(小王)送出的了,而且小红不认识追求她的人,效果已经达到了。

示例结构图

classDiagram
    direction RL
    代理 ..|> 送礼物
    追求者 ..|> 送礼物
    代理 --> 追求者
    class 送礼物 {
        <<interface>>
        + 送洋娃娃()
        + 送鲜花()
        + 送巧克力()
    }

    class 代理 {
        + 送洋娃娃()
        + 送鲜花()
        + 送巧克力()
    }

    class 追求者 {
        + 送洋娃娃()
        + 送鲜花()
        + 送巧克力()
    }

构造模式结构图

classDiagram
    direction BT
    Client --> Subject
    RealSubject --|> Subject
    Proxy --|> Subject
    Proxy --> RealSubject: -realSubject
    note for Subject "Subject 类,定义了 RealSubject 和 Proxy 的共用接口\n这样就在任何使用 RealSubject 的地方都可以使用 Proxy"
    class Subject {
        <<abstract>>
        + request()
    }

    note for RealSubject "RealSubject 类,定义 Proxy 所代表的真实实体"
    class RealSubject {
        + request()
    }

    note for Proxy "Proxy 类,保存一个引用使得代理可以访问实体,并提供一个\n与 Subject 的接口相同的接口,这样代理就可以用来替代实体"
    class Proxy {
        + request()
    }

    class Client {

    }

Subject 类,定义了 RealSubjectProxy 的共用接口,这样就在任何使用 RealSubject 的地方都可以使用 Proxy。

public interface Subject {
    void giveDoll();
}

RealSubject 类,定义 Proxy 所代表的真实实体。

public class RealSubject extends Subject {
    @Override
    public void request() {
        System.out.println("真实的请求");
    }
}

Proxy 类,保存一个引用,使得代理可以访问实体,并提供一个与 Subject 的接口相同的接口,这样代理就可以用来替代实体。

public class Proxy extends Subject {

    RealSubject realSubject;    

    @Override
    public void Request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request();
    }
}

优缺点

优点

  1. 简化客户端的访问。代理对象可以隐藏原始对象的复杂性,使得客户端可以更加简单地访问原始对象。

  2. 降低系统的耦合度。代理对象与原始对象之间进行解耦,使得系统更加灵活,可扩展性更高。

  3. 代理对象可以控制对原始对象的访问。代理对象可以对客户端的请求进行过滤和验证,保证原始对象的安全性。

  4. 提高系统的性能。代理对象可以在访问原始对象之前进行一些预处理,从而提高系统的性能。

缺点

  1. 增加系统的复杂性。代理模式需要增加额外的代理类,使得系统的结构变得更加复杂。

  2. 增加系统的开销。代理模式需要额外的开销来处理请求和响应,从而增加了系统的开销。

  3. 可能降低系统的响应速度。代理对象需要额外的时间来处理请求和响应,从而可能降低系统的响应速度。

适用场景:

  1. 远程代理。当原始对象位于远程服务器上时,可以使用代理模式实现客户端与服务器之间的通信。

  2. 虚拟代理。当创建原始对象需要很长时间时,可以使用代理模式实现延迟加载,即在需要时才创建原始对象。

  3. 安全代理。当需要对原始对象进行安全控制时,可以使用代理模式实现访问控制。

  4. 智能代理。当需要在访问原始对象时添加额外的逻辑处理时,可以使用代理模式实现智能代理。