【问题标题】:Proxy Pattern: how is it more efficent that creating the real object?代理模式:它如何比创建真实对象更有效?
【发布时间】:2014-04-25 15:32:57
【问题描述】:

在以下示例中,来自维基书籍https://en.wikibooks.org/wiki/Computer_Science_Design_Patterns/Proxy

我不确定这比仅创建真实对象并使用其中的显示图像更快/更有效。因为代理无论如何都会在 displayImage 方法中创建真实对象?

//on System B
    class ProxyImage implements Image {

        private RealImage image = null;
        private String filename = null;
        /**
         * Constructor
         * @param FILENAME
         */
        public ProxyImage(final String FILENAME) {
            filename = FILENAME;
        }

        /**
         * Displays the image
         */
        public void displayImage() {
            if (image == null) {
               image = new RealImage(filename);
            }
            image.displayImage();
        }

    }

如果您不使用代理,代理模式肯定不会节省内存,因为它需要实例化两个对象(代理和真实)而不是一个(真实)?

【问题讨论】:

  • 这将在声明时节省内存,仅此而已。可能不值得,但您可以添加 bind() 和 release() 方法为图像动态分配内存。
  • 如何节省内存?我认为我不太了解这种模式
  • 我也不明白这种模式......请阅读答案。
  • 如果您阅读整个示例,例如该页面上的 ProxyExample 类,您将看到答案:IMAGE1.displayImage(); // loading necessary 后跟 IMAGE1.displayImage(); // loading unnecessary

标签: java design-patterns proxy-pattern


【解决方案1】:

来自您发布的链接(强调我的):

代理类ProxyImage在另一个系统上运行,而不是真实图像类本身,并且可以代表那里的真实图像RealImage。从磁盘访问图像信息。使用代理模式,ProxyImage 的代码避免了图像的多次加载,以节省内存的方式从其他系统访问它。

简而言之:它不会节省内存,而是加快应用程序的速度,因为您无需每次都访问磁盘来读取真实图像。

这在这部分代码中得到了证明:

public void displayImage() {
    //if image is not loaded into memory
    if (image == null) {
        //then load it, go to disk only once
        image = new RealImage(filename);
    }
    //now it is in memory, display the real image
    image.displayImage();
}

为了更好地理解这个问题,让我们改变类和接口的定义:

public interface Image {
    String getName();
    byte[] getData();
}

现在,RealImage 类将始终在磁盘中寻找数据,以防文件不存在(它已被删除或重命名):

public class RealImage implements Image {
    //implements all the operations going to disk...
    private String fileName;
    public RealImage(String fileName) {
        this.fileName = fileName;
    }
    @Override
    public String getName() {
        String name = "";
        //fancy operations to seek for the file in disk (in case it has been deleted)
        //read the data from file in disk
        //get the name
        name = ...;
        return name;
    }
    @Override
    public byte[] getData() {
        byte[] data;
        //again, fancy operations to seek for the file in disk (in case it has been deleted)
        //read the data from file in disk
        //get the image data for displaying purposes
        data = ...;
        return data;
    }
}

现在,我们的 ProxyImage 将充当 RealImage 的代理,通过将数据保存到内存中来节省每次访问磁盘的高成本任务:

public class ProxyImage implements Image {
    private String fileName;
    private RealImage realImage;
    private byte[] data;
    private String name;
    //implements all the operations going to disk...
    public RealImage(String fileName) {
        this.fileName = fileName;
    }
    @Override
    public String getName() {
        //in case we don't have the name of the image
        if (this.name == null) {
            //use a RealImage to retrieve the image name
            //we will create the instance of realImage only if needed
            if (realImage == null) {
                realImage = new RealImage(fileName);
            }
            //getting the image from the real image is highly costly
            //so we will do this only once
            this.name = realImage.getName();
        }
        return this.name;
    }
    @Override
    public byte[] getData() {
        //similar behavior for the data of the image
        if (this.data == null) {
            if (realImage == null) {
                realImage = new RealImage(fileName);
            }
            //highly costly operation
            this.data = realImage.getData();
        }
        return this.data;
    }
}

因此反映了为我们的RealImage 使用代理的好处

【讨论】:

  • 所以基本上不是创建您可能不会使用的图像,而是仅在要显示时才创建它?
  • @RNI2013 是的,如果你多次需要它,你不会从磁盘再次创建它,你会重复使用它。
  • 你将如何重复使用它?当您想要显示一个想象并使用它的 displayImage 时,创建真实对象会不会同样有效?因为通过使用代理,您实际上是在实例化两个对象?
  • @RNI2013 你总会有一个权衡:速度与内存使用。如果你想要速度,你会失去记忆,如果你想要节省记忆,你可能会失去速度。在这种情况下,由于您有足够的内存用于您的应用程序(现在大多数设备至少有 2 GB)并且因为创建这个额外的对象就像 4 KB(或类似,取决于 JVM 实现),那么权衡获得的速度是值得的。
  • 好的,谢谢,所以创建两个对象不是什么大问题吗?我只是没有看到代理与创建原始对象并使用其中一种方法相比的优势?我的意思不是在这个例子中我的意思是一般
【解决方案2】:

这个特定代理的目的似乎是实现所谓的“延迟加载”。它实际上并没有读取文件并在内存中创建图像,直到其他一些代码实际尝试显示它。与将图像放入您从不使用的内存中相比,这可以节省大量时间和内存!

在小例子中很容易想到“我可以只编程聪明而不加载愚蠢的东西。”但是想象一个更大的系统,你被一个 API 卡住了,它以 List<Image> 作为参数,但实际上只有在用户单击文件名或其他东西时才会绘制一个。这可能是一个显着的推动力。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    • 1970-01-01
    • 2019-01-29
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 2021-08-03
    相关资源
    最近更新 更多