Proxy Pattern: Interview Questions and Machine Coding Considerations
Common Interview Questions:
Question 1: How would you implement thread-safe lazy initialization in a Virtual Proxy?
if realBook is null:
synchronized(lock):
if realBook is null: // Double-checked locking
realBook = new RealBook(filePath)
return realBook
Key Points: Double-checked locking avoids synchronization overhead on every access after initialization. First check is unsynchronized (fast path). Synchronized block entered only when null. Second check inside ensures only one thread creates the object if multiple threads passed the first check simultaneously. In languages with volatile semantics (Java, C#), mark the reference volatile to prevent instruction reordering issues. Alternative: use lazy initialization holder pattern (static inner class in Java) which is thread-safe by the language specification without explicit locking.
Question 2: Can you chain multiple Proxies? Give an example where this makes sense.
Answer: Yes, Proxies can be chained because each Proxy implements the Subject interface. Example: Client → LoggingProxy → CacheProxy → ProtectionProxy → RealSubject. The LoggingProxy logs all requests. The CacheProxy checks if the result is cached before delegating. The ProtectionProxy validates permissions before allowing the operation. Each Proxy is independent and reusable. However, excessive chaining increases complexity and makes debugging difficult. If you find yourself chaining more than three Proxies, consider whether a single Proxy with composed strategies (Strategy Pattern for access control, caching, logging) would be clearer. The trade-off is flexibility (chaining allows mix-and-match) versus simplicity (single Proxy with configuration is easier to understand).
Question 3: What is the difference between Proxy and Adapter in terms of intent and structure?
Interface: Same as the subject
Relationship: Proxy IS-A Subject (implements same interface)
Delegation: To the real subject
Example: ImageProxy implements Image, controls when RealImage loads
Interface: Different from the adaptee
Relationship: Adapter IS-A Target (implements target interface)
Delegation: To the adaptee (incompatible object)
Example: ShapeAdapter implements Shape, adapts LegacyRectangle (different interface)
If the wrapped object already implements the interface you need, and you are adding control logic, it is a Proxy. If you are converting one interface to another, it is an Adapter. However, a Protection Proxy that wraps a third-party object and also translates its interface combines both patterns (this is acceptable when pragmatism trumps purity).
Machine Coding Considerations:
- Handle null references: In Virtual Proxy, always check if
realSubjectis null before delegating. Fail gracefully if initialization fails (throw descriptive exception or return default/null object). Do not let NullPointerException propagate without context. - Proxy identity vs equality: If clients compare objects using
==(reference equality), Proxy and RealSubject are different objects. Overrideequals()andhashCode()to delegate to RealSubject if semantic equality is needed. Alternatively, document that clients must use the Proxy reference consistently. - Lifecycle management: If RealSubject is expensive or holds resources (file handles, connections), the Proxy should provide cleanup methods. Example:
dispose()orclose()that releases the RealSubject. Make Proxy implement AutoCloseable (Java) or IDisposable (C#) if appropriate. If RealSubject was never created (lazy load not triggered), cleanup is a no-op. - Logging and observability: Add timestamps, user context, and operation names to Proxy logs. This helps trace which operations were proxied and which went directly to RealSubject (if both are accessible). Use structured logging (JSON) for easier parsing in production.
- Testing strategy: Create a MockSubject that implements the Subject interface for unit testing the Proxy logic without expensive RealSubject initialization. Test Proxy separately: verify access control triggers, lazy initialization happens once, caching works correctly. Test RealSubject separately: verify business logic. Integration test: verify Proxy and RealSubject work together correctly.
Common Mistakes to Avoid:
Problem: Violates Liskov Substitution Principle if Proxy overrides methods and changes behavior unexpectedly. Also prevents using multiple Proxies for the same RealSubject (cannot have multiple inheritance).
Fix: Always extract an interface or abstract class that both Proxy and RealSubject implement.
Mistake: Proxy holds business logic instead of just control logic.
Problem: Violates Single Responsibility Principle. Proxy becomes a "god object" that duplicates RealSubject logic or implements unrelated features.
Fix: Proxy should only add control concerns (access, lifecycle, logging). Business logic belongs in RealSubject or should be delegated to separate strategy objects.
Mistake: Forgetting to check if RealSubject is already initialized before creating it again in Virtual Proxy.
Problem: Multiple RealSubject instances are created, wasting resources and causing inconsistent state (each instance may cache different data).
Fix: Use a boolean flag (
isInitialized) or check if reference is null before initializing.