Consider a document editor that can embed graphical objects in a document.
-
Some graphical objects, like large raster images, can be expensive to create.
-
But opening a document should be fast,
-
so we should avoid creating all the expensive objects at once when the document is opened.
This isn't necessary anyway, because not all of these objects will be visible in the document at the same time.
These constraints would suggest creating each expensive object on demand, which in this case occurs when an image becomes visible.
-
But what do we put in the document in place of the image?
-
And how can we hide the fact that the image is created on demand so that we don't complicate the editor's implementation? This optimization shouldn't impact the rendering and formatting code,
- The solution is to use another object,
- an image proxy, that acts as a stand-in for the real image.
- The proxy acts just like the image and takes care of instantiating it when it's required.
The imageProxy
:
-
creates the real image only when the document editor asks it to display itself by invoking its Draw operation.
-
The proxy forwards subsequent requests directly to the image. It must therefore keep a reference to the image after creating it.
Let's assume that images are stored in separate files.
- In this case we can use the file name as the reference to the real object.
- The proxy also stores
- its extent (png, jpg, gif),
- its width and height.
The extent lets the proxy respond to requests for its size from the formatter without actually instantiating the image.
The following class diagram illustrates this example in more detail.
documentEditor
: accesses embedded images through the interface defined by the abstract Graphic class.ImageProxy
- is a class for images that are created on demand.
- maintains the file name as a reference to the image on disk. The file name is passed as an argument to the ImageProxy constructor.
- also stores the bounding box of the image and a reference to the real Image instance.
- This reference won't be valid until the proxy instantiates the real image.
- The Draw operation makes sure the image is instantiated before forwarding it the request.
- GetExtent forwards the request to the image only if it's instantiated; otherwise ImageProxy returns the extent it stores.
class | What it do |
---|---|
Proxy (ImageProxy) |
|
Subject (Graphic) | defines the common interface for RealSubject and Proxy so that a Proxy can be used anywhere a RealSubject is expected. |
RealSubject (Image) | defines the real object that the proxy represents. |
abstract class Graphic {
void displayImage();
}
// On System A
class RealImage implements Graphic {
final String _filename;
RealImage(this._filename) {
_loadImageFromDisk();
}
/// Loads the image from the disk
void _loadImageFromDisk() => print("Loading " + _filename);
/// Displays the image
void displayImage() => print("Displaying " + _filename);
}
// On System B
class ProxyImage implements Graphic {
final String _filename;
RealImage? _image;
ProxyImage(this._filename);
/// Displays the image
void displayImage() {
if (_image == null) {
_image = RealImage(_filename);
} else {
_image!.displayImage();
}
}
}
void main(List<String> arguments) {
Graphic image1 = ProxyImage("HiRes_10MB_Photo1");
Graphic image2 = ProxyImage("HiRes_10MB_Photo2");
print("--- image1----");
image1.displayImage(); // loading necessary
image1.displayImage(); // loading unnecessary
print("--- image2----");
image2.displayImage(); // loading necessary
image2.displayImage(); // loading unnecessary
print("--- image1----");
print("image1.displayImage() again = will display without loading ");
image1.displayImage(); // loading unnecessary
}
// Output
// --- image1----
// Loading HiRes_10MB_Photo1
// Displaying HiRes_10MB_Photo1
// --- image2----
// Loading HiRes_10MB_Photo2
// Displaying HiRes_10MB_Photo2
// --- image1----
// image1.displayImage() again = will display without loading
// Displaying HiRes_10MB_Photo1