Card 08/ 09
Practice: An Editor That Opens Text and Spreadsheet Documents
The editor's open() is written once; each kind of editor creates its own document.
An editor that opens several kinds of document is the Gang of Four's own example of Factory Method, and this exercise asks you to design it. Applying the pattern yourself means deciding which classes play each participant and where the factory method goes, so do that before writing any code.
An editor has an open(path) method that creates a document, loads it from the path, adds it to the list of open documents and reports how many are open. Every editor does those steps the same way. A text editor creates text documents and a spreadsheet editor creates spreadsheets, and more kinds of editor will follow.
- Name the class that plays each participant: creator, concrete creator, product and concrete product.
- Name the factory method and say which class declares it.
- Write
openso that it never namesTextDocumentorSpreadsheetDocument.
Show a solution
Editor is the creator and TextEditor and SpreadsheetEditor are the concrete creators. Document is the product, and TextDocument and SpreadsheetDocument are the concrete products. The factory method is create_document, declared abstract on Editor.
from abc import ABC, abstractmethod
class Document(ABC):
@abstractmethod
def load(self, path: str) -> str: ...
class TextDocument(Document):
def load(self, path: str) -> str:
return f"text document from {path}"
class SpreadsheetDocument(Document):
def load(self, path: str) -> str:
return f"spreadsheet from {path}"
class Editor(ABC):
def __init__(self) -> None:
self.open_documents: list[Document] = []
@abstractmethod
def create_document(self) -> Document: ...
def open(self, path: str) -> None:
document = self.create_document()
self.open_documents.append(document)
print(f"opened {document.load(path)}, {len(self.open_documents)} open")
class TextEditor(Editor):
def create_document(self) -> Document:
return TextDocument()
class SpreadsheetEditor(Editor):
def create_document(self) -> Document:
return SpreadsheetDocument()
TextEditor().open("notes.txt")
SpreadsheetEditor().open("budget.csv")opened text document from notes.txt, 1 open
opened spreadsheet from budget.csv, 1 openA presentation editor would add PresentationEditor and PresentationDocument, and open would not change. A common wrong answer puts the if for the document kind back inside open, which is the conditional the pattern exists to remove.
← → move through sections and cards