Bird
Raised Fist0

Proxy Pattern - Access Control, Lazy Loading, Caching Proxy

LLDeasyInterview freq: medium
Choose your preparation mode4 modes available
🏗️
Proxy Pattern - Access Control, Lazy Loading, Caching Proxy
Use Proxy pattern to control access, defer expensive object creation, and cache results transparently.
easyDesign Patterns - AppliedFreq: medium
Prerequisites:OOP BasicsJava Interfaces and Abstract ClassesJava Concurrency Basics (synchronized, volatile)
⚙️
Functional Requirements
  • Proxy must implement the same interface as RealSubject.
  • Proxy must check client permissions before forwarding calls.
  • Proxy must instantiate RealSubject lazily on first method call.
  • Proxy must cache results of expensive method calls and return cached data on subsequent calls.
  • Proxy must be thread-safe to handle concurrent client calls.
  • Proxy must allow extension to add new behaviors without modifying existing code (Open/Closed Principle).
  • Proxy must handle edge cases such as RealSubject creation failure gracefully.
👤
Use Cases
  • Client requests access to a resource via Proxy.
  • Proxy controls access based on permissions (Access Control).
  • Proxy defers creation of RealSubject until first use (Lazy Loading).
  • Proxy caches results of expensive operations to improve performance (Caching).
  • Multiple clients concurrently access Proxy safely.
  • Proxy denies access if client lacks permission.
🧑‍💻
Actors
  • Client
  • RealSubject (ExpensiveResource)
  • Proxy
❓
Clarifying Questions to Ask
  • How many concurrent clients/threads will access the Proxy?
  • Is the permission check a simple boolean or more complex?
  • Should the cache expire or be invalidated?
  • What happens if RealSubject creation fails?
  • Are multiple methods cached or only specific ones?
  • Is the Proxy expected to be reusable for different RealSubject implementations?
Out of Scope
✗ Distributed proxy implementations.
✗ Network-level proxies or remote method invocation.
✗ Complex permission management systems beyond simple boolean checks.
✗ Persistence of cached data beyond JVM lifecycle.
⚡
QPS Calculation
read qps1000
write qps100
🖥️
Server Estimate
3
Assuming 1000 read requests per second and 100 write requests per second, with caching reducing load. Data volume estimated from average payload size and request rate. Server count based on concurrency and latency requirements.
🔢
Enumerations
enum AccessLevel
NONE | READ | WRITE | ADMIN
Defines permission levels explicitly to avoid string typos and enables exhaustive switch-case for access control.
enum CacheStatus
EMPTY | VALID | STALE
Represents cache state to manage caching lifecycle and invalidation logic clearly.
📦
Subject
0 fields
Defines the common interface for RealSubject and Proxy to implement, enabling polymorphism.
Why thisInterface defines minimal contract for polymorphism in Java, enabling substitution.
Why notAbstract classes would add unnecessary inheritance constraints; interfaces are preferred for flexibility.
String request()Abstract method to be implemented by RealSubject and Proxy to perform the core operation.
📦
RealSubject
1 fields
private String data
Implements the actual resource-intensive operation; simulates expensive object creation and processing.
Why thisConcrete class encapsulates expensive resource logic; direct implementation of Subject interface.
Why notUsing a singleton or static methods would reduce flexibility and testability.
public String request()Performs the expensive operation and returns the result.
📦
Proxy
4 fields
private volatile RealSubject realSubjectprivate AccessLevel clientAccessLevelprivate volatile String cachedDataprivate volatile CacheStatus cacheStatus
Controls access to RealSubject, lazily initializes it, caches results, and ensures thread-safe access.
Why thisProxy pattern enables control and extension without modifying RealSubject; volatile and synchronized ensure thread safety.
Why notDecorator pattern would add behavior but not control access or lazy loading; direct calls lack caching and concurrency control.
public synchronized String request()Checks access, lazily creates RealSubject if needed, returns cached data if valid, else fetches fresh data.
private boolean checkAccess()Checks if client has sufficient AccessLevel to invoke RealSubject.
private void initializeRealSubject()Lazily creates RealSubject instance with double-checked locking and volatile to prevent instruction reordering.
📦
Client
1 fields
private Proxy proxy
Uses Proxy to access RealSubject transparently without knowing about lazy loading or caching.
Why thisClient depends on Proxy interface to decouple from RealSubject implementation details.
Why notDirect RealSubject usage would expose expensive creation and lack access control.
public String makeRequest()Invokes request() on Proxy and handles the returned data.
📦
CacheManager
2 fields
private volatile String cachedValueprivate volatile CacheStatus status
Manages caching lifecycle, cache invalidation, and thread-safe access to cached data.
Why thisSeparates caching concerns from Proxy, enabling single responsibility and easier extension.
Why notEmbedding cache logic in Proxy would reduce modularity and testability.
public synchronized String getCachedData()Returns cached data if valid, else null.
public synchronized void updateCache(String data)Updates cache with new data and marks status VALID.
🔗
Relationships
Proxy◆──►RealSubject[1]
compositionProxy controls RealSubject lifecycle; RealSubject created lazily and destroyed with Proxy
Client──►Proxy[1]
associationClient holds reference to Proxy but Proxy can exist independently
Proxy◇──►CacheManager[1]
aggregationCacheManager can exist independently; Proxy aggregates it for caching
RealSubject──►Subject[1]
realizationRealSubject implements Subject interface
Proxy──►Subject[1]
realizationProxy implements Subject interface
🗄️
AccessControl
PK: clientId
clientId STRINGresourceId STRINGaccessLevel STRINGlastUpdated TIMESTAMP
Stores client permissions for resources.
🗄️
CacheData
PK: resourceId
resourceId STRINGcachedValue STRINGcacheStatus STRINGcacheTimestamp TIMESTAMP
Stores cached results for resources with timestamps.
🔌
GET /resource
Request
{
  "clientId": "string",
  "resourceId": "string"
}
Response
{
  "data": "string",
  "cacheStatus": "string"
}
Returns resource data if client has access; uses cache if valid.
🔌
POST /resource
Request
{
  "clientId": "string",
  "resourceId": "string",
  "data": "string"
}
Response
{
  "success": "boolean",
  "message": "string"
}
Updates resource data and invalidates cache.
🔌
GET /access
Request
{
  "clientId": "string",
  "resourceId": "string"
}
Response
{
  "accessLevel": "string"
}
Returns access level of client for resource.

Practice

(1/5)
1. A class has a private constructor and a static synchronized getInstance() method returning a single instance. Which pattern is applied and why is this the CORRECT use case?
easy
A. Prototype pattern, because it clones existing instances to create new ones.
B. Factory Method pattern, because it controls object creation through a static method.
C. Builder pattern, because it constructs complex objects step-by-step.
D. Singleton pattern, because it ensures only one instance exists with controlled access.

Solution

  1. Step 1: Identify Singleton characteristics

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance with thread safety.
  2. Step 2: Eliminate other creational patterns

    Factory Method controls subclass instantiation, Builder separates construction from representation, and Prototype clones objects; none enforce a single instance.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Private constructor + static synchronized getInstance() -> Singleton pattern [check]
Hint: Private constructor + static getInstance() = Singleton [check]
Common Mistakes:
2. The Food Ordering System needs to add a WeekendFeeStrategy to apply extra charges on weekends. Given the current design uses a FeeStrategy interface for pricing, what is the minimum change required to add this feature while respecting SOLID principles?
medium
A. Add a boolean weekend flag to the Order class and update all pricing logic accordingly.
B. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it where needed without modifying existing classes.
C. Combine weekend fee logic inside the OrderManager class to centralize pricing.
D. Modify the existing FeeStrategy implementation to add weekend logic with if-else checks.

Solution

  1. Step 1: Identify the SOLID principle involved

    The Open-Closed Principle (OCP) requires that existing code should not be modified to add new behavior.
  2. Step 2: Evaluate options against OCP

    Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it where needed without modifying existing classes. respects OCP by adding a new strategy class without modifying existing ones. Add a boolean weekend flag to the Order class and update all pricing logic accordingly. modifies Order class and pricing logic, violating OCP. Modify the existing FeeStrategy implementation to add weekend logic with if-else checks. and D modify existing classes, violating OCP.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class -> OCP respected, no existing class changes [check]
Hint: New strategy class -> respect OCP, avoid modifying existing code [check]
Common Mistakes:
3. The brokerage system currently calculates fees using a FeeCalculator class. To add a new WeekendFeeStrategy without modifying existing code, which SOLID principle applies and what is the minimum change required?
medium
A. Open/Closed Principle: Create a new WeekendFeeStrategy class implementing FeeStrategy interface; FeeCalculator uses polymorphism.
B. Single Responsibility Principle: Modify FeeCalculator to handle weekend fees explicitly.
C. Dependency Inversion Principle: Inject concrete WeekendFeeStrategy directly into FeeCalculator constructor.
D. Liskov Substitution Principle: Replace FeeCalculator with WeekendFeeCalculator subclass overriding fee logic.

Solution

  1. Step 1: Identify the SOLID principle for extensibility

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: How to apply OCP here

    Create a new class WeekendFeeStrategy implementing FeeStrategy interface; FeeCalculator uses polymorphism to apply different strategies without modification.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New strategy class + polymorphism -> OCP compliance [check]
Hint: Add new fee strategy class, no FeeCalculator changes [check]
Common Mistakes:
4. To support ElectricCar needing ChargingSpot in the vending machine parking design, what is the minimum change that respects extensibility without modifying existing classes?
hard
A. Create a new ChargingSpot class extending Spot and modify ParkingLot to handle it specially.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly.
C. Modify existing Vehicle and Spot classes to add electric-specific fields and logic.
D. Add a boolean isElectric flag to Vehicle and Spot classes and add conditional logic everywhere.

Solution

  1. Step 1: Identify extensibility requirements

    Adding new vehicle and spot types without modifying existing classes preserves OCP and avoids class explosion.
  2. Step 2: Evaluate options for minimal invasive change

    Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly. adds new enum values and a subclass, updating compatibility map without changing existing classes. Others modify existing classes or add flags causing complexity.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    New enums and subclass extend design without modifying existing classes [check]
Hint: Add enums + subclass + compatibility map, no existing class changes [check]
Common Mistakes:
5. You need to add support for ElectricCar and ChargingSpot in an existing parking system using Observer pattern. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Add a boolean isElectric flag to Vehicle and Spot classes and update existing logic to handle charging spots.
B. Modify Vehicle and Spot classes to include electric-specific fields and logic for charging compatibility.
C. Create a new ElectricCar class and ChargingSpot class, then modify the Subject to notify these new types explicitly.
D. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally.

Solution

  1. Step 1: Identify extensibility requirements

    Adding ElectricCar and ChargingSpot should not modify existing classes to respect OCP.
  2. Step 2: Evaluate options

    Modify Vehicle and Spot classes to include electric-specific fields and logic for charging compatibility. and D modify existing classes, violating OCP; Create a new ElectricCar class and ChargingSpot class, then modify the Subject to notify these new types explicitly. modifies Subject, increasing coupling.
  3. Step 3: Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType enum, create ElectricCar subclass, and update SpotCompatibility map externally. adds new enum values and subclasses, updating compatibility externally, preserving existing class integrity.

  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    New enums and subclasses added externally -> existing classes unchanged [check]
Hint: Add enums and subclasses externally, keep existing classes untouched [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard