Card 07/ 08
Practice: Splitting a Storage Interface
A read-only cache that must still implement write, delete and backup.
Splitting an interface by its clients is the skill practised here. A Storage interface declares read, write, delete and backup. A ReadOnlyCache serves prices from a snapshot and cannot change it. Three clients use storage: a price lookup that only reads, a sync job that reads and writes, and a nightly job that only backs up. Answer three questions: which methods of the cache are forced, what interfaces would you create, and what does the sync job take?
from abc import ABC, abstractmethod
class Storage(ABC):
@abstractmethod
def read(self, key): ...
@abstractmethod
def write(self, key, value): ...
@abstractmethod
def delete(self, key): ...
@abstractmethod
def backup(self): ...
class ReadOnlyCache(Storage):
def __init__(self, snapshot):
self.snapshot = snapshot
def read(self, key):
return self.snapshot.get(key)
def write(self, key, value):
raise NotImplementedError("cache is read-only")
def delete(self, key):
raise NotImplementedError("cache is read-only")
def backup(self):
pass
cache = ReadOnlyCache({"price:pen": "1.20"})
print(cache.read("price:pen"))
try:
cache.write("price:pen", "1.50")
except NotImplementedError as error:
print(f"NotImplementedError: {error}")1.20
NotImplementedError: cache is read-onlyShow the answers
The forced methods. write and delete raise, and backup silently does nothing, which is worse: a backup job given the cache would report success having saved nothing.
The interfaces. Split by client: Reader with read, Writer with write and delete, and Backup with backup. Keep write and delete together, because the only client that changes data uses both.
The sync job. It reads and writes, so declare ReadWriteStorage as an interface extending Reader and Writer, and have the sync job take that. The price lookup takes Reader, which the cache implements and nothing more. The cache's three stub methods are deleted.
← → move through sections and cards