Extension Architecture
Goals
- Extension changes should be predictable and localized.
- New features should not break stable paths.
Guidance
- Use a registry or mapping instead of long if-else chains.
- Expose extension points as explicit interfaces.
- Keep the default path simple; add extensions as optional paths.
- If you use plugin loading, define when and how it loads.
Example
A growing if-else chain spreads change across the core path:
# main.py
import itertools
from sources import HNSource, V2Source, RedditSource
class NewsGrabber:
def get_news(self, source: Optional[str] = None) -> Iterable[News]:
if source is None:
return itertools.chain(HNSource().iter_news(), V2Source().iter_news(), RedditSource().iter_news())
if source == 'HN':
return HNSource().iter_news()
elif source == 'V2':
return V2Source().iternews()
elif source == 'Reddit':
return RedditSource().iternews()
else:
raise ValueError(f"Not supported source: {source}")A registry centralizes extension points:
# sources/base.py
source_map: Dict[str, BaseSource] = {}
def register(source_cls):
source_map[source_cls.name] = source_cls()
return source_cls# sources/hn.py
from sources.base import BaseSource, register
@register
class HNSource(BaseSource):
name = "HN"
# omitted