WalzoneInterview Prep
📞 Interviewing soon? Practice with a realistic AI mock phone interview — it calls you, then scores you. First 15 min FREE →

Design Patterns · Advanced · question 56 of 100

How can you use the Proxy pattern to implement caching, and what challenges might you face in maintaining cache consistency?

📕 Buy this interview preparation book: 100 Design Patterns questions & answers — PDF + EPUB for $5

The Proxy pattern allows us to provide a surrogate or placeholder for an object in order to control its access. Using the Proxy pattern, we can implement caching by creating a proxy object that serves as a cache for the actual object. When a client requests an object, the proxy checks if the object is available in the cache. If the object is found in the cache, the proxy returns the cached object without accessing the actual object. If the object is not found in the cache, the proxy retrieves it from the actual object, caches it, and then returns it to the client.

Here’s an example of how we can use the Proxy pattern to implement caching in Java:

public interface DataService {
    public List<String> getData();
}

public class DataServiceImpl implements DataService {
    public List<String> getData() {
        // code to retrieve data from a database or a web service
        // ...
    }
}

public class DataProxy implements DataService {
    private DataService actualService;
    private List<String> cachedData;

    public DataProxy(DataService actualService) {
        this.actualService = actualService;
    }

    public List<String> getData() {
        if (cachedData == null) {
            cachedData = actualService.getData();
        }
        return cachedData;
    }
}

In this example, we have an interface ‘DataService‘ that defines the contract for retrieving data. We also have a concrete implementation ‘DataServiceImpl‘ that retrieves data from a database or a web service. Finally, we have a proxy ‘DataProxy‘ that implements the ‘DataService‘ interface and maintains a cache of the retrieved data.

The ‘DataProxy‘ constructor takes an object of the ‘DataService‘ interface as a parameter, which we refer to as the actual service. The ‘getData()‘ method first checks if the cached data is available. If the cached data is not available, the proxy requests the actual service to retrieve the data, caches the retrieved data, and returns it to the client. If the cached data is available, the proxy returns it directly to the client without accessing the actual service.

One of the challenges of using the Proxy pattern for caching is maintaining cache consistency. If the actual object changes, we need to invalidate the cached data to ensure that subsequent requests retrieve the updated data. One way to handle this is to set an expiration time for the cached data so that it is refreshed after a certain period of time. Another way is to use an event-driven approach to invalidate the cached data whenever the actual object changes.

public class DataProxy implements DataService {
    private DataService actualService;
    private List<String> cachedData;
    private long lastUpdate;

    public DataProxy(DataService actualService) {
        this.actualService = actualService;
        lastUpdate = 0;
    }

    public synchronized List<String> getData() {
        if (cachedData == null || System.currentTimeMillis() - lastUpdate > CACHE_EXPIRATION_TIME) {
            cachedData = actualService.getData();
            lastUpdate = System.currentTimeMillis();
        }
        return cachedData;
    }

    public synchronized void invalidateCache() {
        cachedData = null;
        lastUpdate = 0;
    }
}

In this modified example, we have added a ‘lastUpdate‘ field to keep track of the time when the data was last retrieved from the actual service. The ‘getData()‘ method now checks if the cached data has expired based on the ‘CACHE_EXPIRATION_TIME‘ constant, and if it has, retrieves the data from the actual service, caches it, and updates ‘lastUpdate‘.

We have also added a new ‘invalidateCache()‘ method that invalidates the cached data by setting it to ‘null‘ and resetting ‘lastUpdate‘ to ‘0‘. This method can be called whenever the actual object changes to ensure that subsequent requests retrieve the updated data.

In summary, we can use the Proxy pattern to implement caching by creating a proxy object that caches the retrieved data from the actual object. However, we need to be careful to maintain cache consistency by either setting an expiration time or using an event-driven approach to invalidate the cached data whenever the actual object changes.

Reading is step one. Saying it out loud is the interview. Our AI interviewer calls your phone and runs a realistic Design Patterns interview — then scores it.
📞 Practice Design Patterns — free 15 min
📕 Buy this interview preparation book: 100 Design Patterns questions & answers — PDF + EPUB for $5

All 100 Design Patterns questions · All topics