← The Trade Desk Interview Insights
My first instinct was to just describe a doubly linked list plus hashmap setup, which is the standard answer, but that wasn't really the point.
Start by clarifying requirements: what does 'pluggable' mean (runtime vs compile-time, per-cache vs global)? Then propose a design that separates the eviction policy from the cache storage and access logic, using interfaces and dependency injection. Discuss trade-offs like performance overhead, thread safety, and how to handle policy-specific metadata.
Pro tip: Mention that you would keep the core cache operations (get/put) policy-agnostic and use a strategy pattern, but also consider using a policy that can maintain its own data structures (e.g., for LFU) without bloating the cache. This shows you understand separation of concerns and performance implications.
Ask questions to understand what 'pluggable' means: should policies be swappable at runtime? Per-cache instance or global? What are the performance and thread-safety requirements? This ensures you design the right solution.
Propose an interface (e.g., EvictionPolicy) with methods like onGet(key), onPut(key, value), evict(), and possibly remove(key). This abstracts the policy from the cache.
The cache should handle storage (e.g., a map) and delegate eviction decisions to the policy. The policy may need access to cache metadata (e.g., access order) or maintain its own structures.
Show how LRU, LFU, FIFO, etc., can be implemented as separate classes implementing the interface. For LRU, use a doubly linked list + hashmap; for LFU, use frequency buckets.
Address performance overhead (e.g., extra method calls), thread safety (synchronization or concurrent data structures), and how to configure policies (factory, dependency injection, or configuration file).
AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.