Design Patterns in Dart: Proxy Pattern

What is the Proxy Pattern?

The Proxy Pattern is a structural design pattern that:

Provides a substitute or placeholder for another object to control access to it.

It’s like placing a gatekeeper between the client and the real object. This “proxy” can add additional logic (like caching, lazy initialization, access control, logging…) before forwarding the request.


Real-World Analogy

Think of a security guard at the door of a high-security room:

  • Visitors don’t talk to the people inside directly.
  • They go through the guard who checks credentials, logs the visit, and only then allows access.

In software, the guard is your proxy.


When to Use the Proxy Pattern

  • You want to control access to an object (security, permissions, etc.).
  • You want to add lazy initialization or caching logic.
  • You want to add logging or monitoring without touching the original object.
  • The actual object is expensive to create or located remotely (e.g., over a network).

Types of Proxies

Proxy TypeDescription
Virtual ProxyControls access to resource-heavy objects (e.g., images)
Protection ProxyChecks access rights (e.g., auth, permissions)
Remote ProxyControls access to remote objects (e.g., via API)
Smart ProxyAdds extra behaviors like caching, counting, logging

Dart Example: Virtual Proxy for an Image Viewer

Suppose you are building an image viewer app. Large images are expensive to load, so you want to delay loading until needed.


Step 1: Define the Subject Interface

abstract class Image {
void display();
}

Step 2: Create the Real Object

class RealImage implements Image {
final String filename;

RealImage(this.filename) {
_loadFromDisk();
}

void _loadFromDisk() {
print("Loading image from disk: $filename");
}

@override
void display() {
print("Displaying: $filename");
}
}

Step 3: Create the Proxy Object

class ProxyImage implements Image {
final String filename;
RealImage? _realImage;

ProxyImage(this.filename);

@override
void display() {
_realImage ??= RealImage(filename); // lazy load
_realImage!.display();
}
}

Step 4: Client Code

void main() {
Image image1 = ProxyImage("mountains.jpg");
Image image2 = ProxyImage("beach.jpg");

print("Image1 will be displayed:");
image1.display(); // Loads + displays

print("\nImage1 again:");
image1.display(); // Just displays (already loaded)

print("\nImage2 will be displayed:");
image2.display(); // Loads + displays
}

Output

Image1 will be displayed:
Loading image from disk: mountains.jpg
Displaying: mountains.jpg

Image1 again:
Displaying: mountains.jpg

Image2 will be displayed:
Loading image from disk: beach.jpg
Displaying: beach.jpg

Key Benefits of Proxy Pattern

BenefitDescription
Lazy LoadingLoad resources only when necessary
Access ControlPrevent unauthorized use
LoggingTrack usage without modifying original object
CachingStore computed results to avoid repetition

Drawbacks

  • Adds extra layers of abstraction.
  • May become hard to maintain if overused.
  • Performance overhead if proxies are nested or complex.

Dart & Flutter Use Cases

  • Image.network in Flutter uses internal proxying and caching.
  • A Repository class can use a proxy to check authentication before fetching data.
  • Analytics proxies can log events when users perform actions without modifying main UI logic.
  • You can create a secure proxy to protect admin-only services.

Summary

PropertyProxy Pattern
Pattern TypeStructural
Core IdeaSubstitute the real object with a handler
Key UseControl access, lazy load, caching
In FlutterGreat for optimizing performance

Comparison with Other Patterns

PatternDifference
FacadeSimplifies subsystem usage
DecoratorAdds behavior dynamically without hiding object
AdapterConverts one interface to another
ProxyControls access to original object (can wrap or delay it)

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Lên đầu trang