Pluggable Backends (Strategy Pattern)

Step 5 β€” Pluggable Backends

Right now the storage is hardcoded. In real systems you'll swap a dict for an LSM tree, an on-disk B-tree, or a networked shard. That's the Strategy pattern.

Define an abstract StorageBackend with put / get / delete / keys. Implement two concrete backends: a plain DictBackend and a SortedDictBackend that always returns keys in alphabetical order. Then make KeyValueStore accept any backend and delegate to it.

Hints
Starter code
from abc import ABC, abstractmethod

class StorageBackend(ABC):
    """Strategy interface for storage backends."""

    @abstractmethod
    def put(self, key, value):
        pass

    @abstractmethod
    def get(self, key):
        pass

    @abstractmethod
    def delete(self, key):
        pass

    @abstractmethod
    def keys(self):
        pass

class DictBackend(StorageBackend):
    """In-memory dict storage."""
    # TODO: Implement using a plain dict
    pass

class SortedDictBackend(StorageBackend):
    """Dict storage; keys() returns keys in alphabetical order."""
    # TODO: Implement using a dict, but sort keys() on return
    pass

class KeyValueStore:
    """KV store that accepts any StorageBackend strategy."""

    def __init__(self, backend: StorageBackend):
        # TODO: Keep a reference to the backend
        pass

    def put(self, key, value):
        pass

    def get(self, key):
        pass

    def delete(self, key):
        pass

    def keys(self):
        pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.