Notes and exercises for learning design patterns
The factory no longer uses a long if / elif chain.
Instead, it stores the mapping between file extensions and importer classes in one dictionary.
_importers = {
".txt": PlainTextDocumentImporter,
".md": MarkdownDocumentImporter,
".html": HtmlDocumentImporter,
".json": JsonDocumentImporter,
}
This makes the decision table easier to see.
class DocumentImporterFactory:
_importers = {
".txt": PlainTextDocumentImporter,
".md": MarkdownDocumentImporter,
".html": HtmlDocumentImporter,
".json": JsonDocumentImporter,
}
@classmethod
def create_for_file(cls, path: str) -> DocumentImporter:
suffix = Path(path).suffix.lower()
try:
importer_class = cls._importers[suffix]
except KeyError:
raise ValueError(f"Unsupported document type: {path}") from None
return importer_class()
The registry stores classes:
".md": MarkdownDocumentImporter
not instances:
".md": MarkdownDocumentImporter()
This lets the factory create a fresh importer each time:
return importer_class()
That is useful if importers later have per-instance state.
For stateless importers, storing instances could work, but storing classes is a good default for this exercise.
Not completely.
If we add XML support, we still need to modify the registry:
_importers = {
".txt": PlainTextDocumentImporter,
".md": MarkdownDocumentImporter,
".html": HtmlDocumentImporter,
".json": JsonDocumentImporter,
".xml": XmlDocumentImporter,
}
So the factory is still not fully closed for modification.
However, the registry is still an improvement over a long conditional because the mapping is explicit and easy to scan.
The registry version is a middle step.
It improves this:
if suffix == ".txt":
return PlainTextDocumentImporter()
if suffix == ".md":
return MarkdownDocumentImporter()
if suffix == ".html":
return HtmlDocumentImporter()
into this:
_importers = {
".txt": PlainTextDocumentImporter,
".md": MarkdownDocumentImporter,
".html": HtmlDocumentImporter,
}
The design is still centralized.
That is often good enough for a small or stable application.
If the list of importers changes frequently, then a registration-based approach may be better.
A registry dictionary makes a factory easier to maintain and read, but it does not completely remove the need to modify the factory when new implementations are added.
It is cleaner organization, not full Open/Closed Principle compliance.