Proxy Pattern
The Proxy design pattern is used to control access to an object by acting as an intermediary or placeholder.
It adds an additional layer of protection or functionality to the original object without changing its core behavior.
Think of it as having a personal assistant (proxy) who can handle certain tasks on your behalf while still allowing you to interact with the main tasks directly.
Table of Contents
Proxy Pattern C++ Example:
Let’s see a simple example of the Proxy pattern to demonstrate how it controls access to an expensive or sensitive object, a RealImage
, by using a proxy, ImageProxy
.
#include <iostream>
#include <string>
// Subject Interface: Image (Abstract class or Interface)
class Image {
public:
virtual void display() const = 0;
virtual ~Image() {}
};
// Real Subject: RealImage (Expensive or Sensitive object)
class RealImage : public Image {
public:
RealImage(const std::string& fileName) : fileName_(fileName) {
loadImageFromDisk();
}
void display() const override {
std::cout << "Displaying image: " << fileName_ << std::endl;
}
private:
void loadImageFromDisk() {
// Simulate loading image from disk (expensive operation)
std::cout << "Loading image from disk: " << fileName_ << std::endl;
}
std::string fileName_;
};
// Proxy: ImageProxy (Controls access to RealImage)
class ImageProxy : public Image {
public:
ImageProxy(const std::string& fileName) : fileName_(fileName), realImage_(nullptr) {}
void display() const override {
if (!realImage_) {
realImage_ = new RealImage(fileName_);
}
realImage_->display();
}
~ImageProxy() {
delete realImage_;
}
private:
std::string fileName_;
RealImage* realImage_;
};
int main() {
// Create an ImageProxy and display the image (RealImage is not loaded yet)
Image* image = new ImageProxy("example.jpg");
image->display(); // Output: Loading image from disk: example.jpg
std::cout << std::endl;
// Display the image again (RealImage is now loaded from the proxy)
image->display(); // Output: Displaying image: example.jpg
// Clean up
delete image;
return 0;
}
Proxy Pattern Explanation:
In this example, we have a Image
interface (Subject) that defines the common display()
method for both the RealImage
and the ImageProxy
.
The RealImage
(Real Subject) is the expensive or sensitive object that loads an image from disk. It implements the Image
interface and provides the core functionality for displaying an image.
The ImageProxy
(Proxy) controls access to the RealImage
. It holds a reference to the RealImage
and only creates it when needed. If the RealImage
is not yet loaded, the ImageProxy
loads it from disk on demand. Otherwise, it uses the existing RealImage
to display the image.
In the main()
function, we demonstrate how the Proxy pattern works. We create an ImageProxy
and call its display()
method. The first time we call display()
, the ImageProxy
loads the image from disk (RealImage). The second time we call display()
, the ImageProxy
uses the already loaded RealImage
to display the image, avoiding the costly loading process.
Applications of the Proxy Design Pattern:
- Virtual Proxy: Used to delay the creation of expensive objects until they are actually needed.
- Remote Proxy: Acts as a local representation of a remote object, providing access to it over the network.
- Protection Proxy: Controls access to sensitive objects, enforcing access control rules.
- Logging Proxy: Used to log method calls on objects for debugging or monitoring purposes.
Pros of the Proxy Design Pattern:
- Access Control: Proxy can control access to the real object, providing additional security.
- Lazy Initialization: Proxy can defer the creation of the real object until it is actually needed.
- Reduced Memory Usage: Proxy pattern can reduce memory usage by creating expensive objects only when necessary.
- Decoupling: Proxy separates the client code from the real object, promoting a more flexible design.
Cons of the Proxy Design Pattern:
- Complexity: Introducing a proxy may add some complexity to the system.
- Performance Overhead: In certain cases, proxy pattern might introduce performance overhead due to additional checks or operations.
- Maintenance: The presence of multiple proxy classes can make the codebase more challenging to maintain.
In summary, the Proxy pattern is useful when you want to add an additional layer of control or functionality to an existing object. It provides a way to manage access to expensive or sensitive objects and allows for lazy initialization and other optimizations. However, it’s essential to consider the trade-offs and use the pattern judiciously to avoid unnecessary complexity.