Problem: Cache Data Using Selectable Backends

Medium
30 min
Build a caching service that can switch between in-memory, file-based, and no-op caching backends without changing application logic.

Problem statement

Your team’s application needs a caching layer—but deployment environments differ:

  • Development should use an in-memory cache for speed.

  • Production must persist cache data between restarts via a file-based cache.

  • Testing should use a no-op cache that does nothing, so tests don’t leave traces.

Many applications hardcode their caching behavior with if statements, making it difficult to adapt across environments. Each new backend forces more code changes. Build a configurable caching system using the Strategy Pattern, allowing each backend to operate independently and be swapped at runtime.

Goal

Implement both parts of the pattern:

  1. Strategies:

    • MemoryCache: Stores key–value pairs in memory.
    • FileCache: Simulates persistence by saving cache data to a JSON file (you can use Node’s fs module).
    • NullCache: A no-op cache for testing that ignores .set() calls and always returns null from .get().

    Each must implement two methods:

    • .get(key): Returns the cached value or null.
    • .set(key, value): Stores the value under the given key.
  2. Context:

    • CacheService: The context class that delegates all caching operations to the active strategy.
    • Must allow runtime swapping via .setStrategy().

Constraints

  • No conditional logic (if or switch) inside CacheService.

  • All caching logic must live entirely within each strategy.

  • Strategies must not share state or call each other.

  • For FileCache, use synchronous fs operations (readFileSync / writeFileSync) for simplicity.

Sample output

The examples below illustrate what the output should look like:

const service = new CacheService(new MemoryCache());
service.set('user:1', { name: 'Alice' });
console.log('Memory:', service.get('user:1')); // { name: 'Alice' }
/* Expected output:
Memory: { name: 'Alice' }
*/
service.setStrategy(new NullCache());
service.set('user:2', { name: 'Bob' });
console.log('Null:', service.get('user:2')); // null
/* Expected output:
Null: null
*/
service.setStrategy(new FileCache());
service.set('session:1', { token: 'xyz' });
console.log('File:', service.get('session:1')); // { token: 'xyz' }
/* Expected output:
File: { token: 'xyz' }
*/

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Problem: Cache Data Using Selectable Backends

Medium
30 min
Build a caching service that can switch between in-memory, file-based, and no-op caching backends without changing application logic.

Problem statement

Your team’s application needs a caching layer—but deployment environments differ:

  • Development should use an in-memory cache for speed.

  • Production must persist cache data between restarts via a file-based cache.

  • Testing should use a no-op cache that does nothing, so tests don’t leave traces.

Many applications hardcode their caching behavior with if statements, making it difficult to adapt across environments. Each new backend forces more code changes. Build a configurable caching system using the Strategy Pattern, allowing each backend to operate independently and be swapped at runtime.

Goal

Implement both parts of the pattern:

  1. Strategies:

    • MemoryCache: Stores key–value pairs in memory.
    • FileCache: Simulates persistence by saving cache data to a JSON file (you can use Node’s fs module).
    • NullCache: A no-op cache for testing that ignores .set() calls and always returns null from .get().

    Each must implement two methods:

    • .get(key): Returns the cached value or null.
    • .set(key, value): Stores the value under the given key.
  2. Context:

    • CacheService: The context class that delegates all caching operations to the active strategy.
    • Must allow runtime swapping via .setStrategy().

Constraints

  • No conditional logic (if or switch) inside CacheService.

  • All caching logic must live entirely within each strategy.

  • Strategies must not share state or call each other.

  • For FileCache, use synchronous fs operations (readFileSync / writeFileSync) for simplicity.

Sample output

The examples below illustrate what the output should look like:

const service = new CacheService(new MemoryCache());
service.set('user:1', { name: 'Alice' });
console.log('Memory:', service.get('user:1')); // { name: 'Alice' }
/* Expected output:
Memory: { name: 'Alice' }
*/
service.setStrategy(new NullCache());
service.set('user:2', { name: 'Bob' });
console.log('Null:', service.get('user:2')); // null
/* Expected output:
Null: null
*/
service.setStrategy(new FileCache());
service.set('session:1', { token: 'xyz' });
console.log('File:', service.get('session:1')); // { token: 'xyz' }
/* Expected output:
File: { token: 'xyz' }
*/

Good luck trying the problem! If you’re unsure how to proceed, check the “Solution” tab above.

Node.js
const fs = require('fs');
// --- Strategies (implement these) ---
class MemoryCache {}
class FileCache {}
class NullCache {}
// --- Context (implement this too) ---
class CacheService {
constructor(strategy) {}
setStrategy(strategy) {}
get(key) {}
set(key, value) {}
}
// Example usage
const service = new CacheService(new MemoryCache());
service.set('user:1', { name: 'Alice' });
console.log('Memory:', service.get('user:1')); // { name: 'Alice' }
/* Expected output:
Memory: { name: 'Alice' }
*/
service.setStrategy(new NullCache());
service.set('user:2', { name: 'Bob' });
console.log('Null:', service.get('user:2')); // null
/* Expected output:
Null: null
*/
service.setStrategy(new FileCache());
service.set('session:1', { token: 'xyz' });
console.log('File:', service.get('session:1')); // { token: 'xyz' }
/* Expected output:
File: { token: 'xyz' }
*/