• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

图解设计模式(21):Proxy模式(只在必要时生成实例)

设计模式 winrains 1年前 (2019-09-24) 51次浏览

Proxy是“代理人”的意思,指的是代替别人进行工作的人。当不一定需要本人亲自进行工作时,就可以寻找代理人去完成工作。当代理人遇到无法自己解决的事情时,就会去找本人解决该问题。

1 Proxy模式中的角色

  • Subject(主体)

定义了使Proxy角色和RealSubject角色之间具有一致性的接口。由于存在Subject角色,所以Client角色不必在意它所使用的究竟是Proxy角色还是RealSubject角色。在示例中,对应Printable接口。

  • Proxy(代理人)

会尽量处理来自Client角色的请求。只有当自己不能处理时,它才会将工作交给RealSubject角色。Proxy角色只有在必要时才会生成RealSubject角色。Proxy角色实现了在Subject角色中定义的接口。在示例中,对应PrinterProxy类。

  • RealSubject(实际的主体)

“本人”RealSubject角色会在“代理人”Proxy角色无法胜利工作时出场。它与Proxy角色一样,也实现了在Subject角色中定义的接口。在示例中,对应Printer类。

  • Client(请求者)

使用Proxy模式的角色。在示例中,对应Main类。

2 Proxy模式的类图

3 示例程序

3.1 类和接口一览表

名字 说明
Printer 表示带有名字的打印机的类(本人)
Printable Printer和PrinterProxy共同的接口
PrinterProxy 表示带有名字的打印机的类(代理人)
Main 测试程序行为的类

3.2 类图

3.3 示例代码

Printer类

表示“本人”的类。
在构造方法中,让它做一些“重活”(heavyJob)。heavyJob是一个干5秒“重活”的方法,每秒以点号(.)显示一次干活的进度。

public class Printer implements Printable {
    private String name;
    public Printer() {
        heavyJob("正在生成Printer的实例");
    }
    public Printer(String name) {                   // 构造函数
        this.name = name;
        heavyJob("正在生成Printer的实例(" + name + ")");
    }
    public void setPrinterName(String name) {       // 设置名字
        this.name = name;
    }
    public String getPrinterName() {                // 获取名字
        return name;
    }
    public void print(String string) {              // 显示带打印机名字的文字
        System.out.println("=== " + name + " ===");
        System.out.println(string);
    }
    private void heavyJob(String msg) {             // 重活
        System.out.print(msg);
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.print(".");
        }
        System.out.println("结束。");
    }
}

Printable接口

用于使PrinterProxy类和Printer类具有一致性。

public interface Printable {
    public abstract void setPrinterName(String name);   // 设置名字
    public abstract String getPrinterName();            // 获取名字
    public abstract void print(String string);          // 显示文字(打印输出)
}

PrintProxy类

扮演“代理人”角色的类,实现了Printable接口。
name字段中保存了打印机的名字,而real字段中保存的是“本人”。
print方法已经超出了代理人的工作范围,因此它会调用realize方法来生成本人。
不论setPrinterName方法和getPrinterName方法被调用多少次,都不会生成Printer类的实例。只有当真正需要本人时,才会生成Printer类的实例(PrinterProxy类的调用者完全不知道是否生成了本人,也不用在意是否生成了本人)。

public class PrinterProxy implements Printable {
    private String name;            // 名字
    private Printer real;           // “本人”
    public PrinterProxy() {
    }
    public PrinterProxy(String name) {      // 构造函数
        this.name = name;
    }
    public synchronized void setPrinterName(String name) {  // 设置名字
        if (real != null) {
            real.setPrinterName(name);  // 同时设置“本人”的名字
        }
        this.name = name;
    }
    public String getPrinterName() {    // 获取名字
        return name;
    }
    public void print(String string) {  // 显示
        realize();
        real.print(string);
    }
    private synchronized void realize() {   // 生成“本人”
        if (real == null) {
            real = new Printer(name);
        }
    }
}

Main类

测试程序。

public class Main {
    public static void main(String[] args) {
        Printable p = new PrinterProxy("Alice");
        System.out.println("现在的名字是" + p.getPrinterName() + "。");
        p.setPrinterName("Bob");
        System.out.println("现在的名字是" + p.getPrinterName() + "。");
        p.print("Hello, world.");
    }
}

运行结果

现在的名字是Alice。
现在的名字是Bob。
正在生成Printer的实例(Bob).....结束。
=== Bob ===
Hello, world.

4 总结

4.1 使用代理人来提升处理速度

通过使用Proxy角色,可以将耗时处理推迟至真正需要某个功能时才将其初始化,可以帮助改善用户体验。

4.2 各种Proxy模式

  • Virtual Proxy(虚拟代理)

与本示例相同,只有当真正需要实例时,才生成和初始化实例。

  • Remote Proxy(远程代理)

如同本地方法调用一样,去调用远程网络的对象。Java的RMI(远程方法调用)就相当于Remote Proxy。

  • Access Proxy

用于在调用RealSubject角色的功能时设置访问限制。例如,可以只允许指定的用户调用方法,其他用户调用时则报错。

摘自《图解设计模式》


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)