Advanced Project 4 — Document Conversion Pipeline¶
Difficulty: Advanced · Estimated effort: 2 weeks · Prerequisites: Module 11 — RAII and Smart Pointers, Module 13 — Exception Handling, Module 15 — SOLID and Design Patterns · Demanded pattern decisions: chain of responsibility, abstract factory
This is one of the designated integration projects, and one of the projects that requires a design-pattern decision: at least two patterns must be named and justified by a requirement, and one alternative must be considered and declined in writing.
No complete solution is provided. The stage model is the design decision, and it is not made for you here.
1. Scenario¶
A department converts documents between formats for an archive: plain text with markup, a simplified Markdown-like format, and a comma-separated tabular format. The conversion is not one step but several — normalise whitespace, expand includes, apply a template, reflow to a width, then serialise — and different archives want different stage sets.
The current tool is a single function containing five transformations and every combination of flags, and adding a stage means editing the flags, the help text and the tests.
2. Problem statement¶
Build a conversion pipeline: a document model in the middle, readers that produce it, stages that transform it, writers that serialise it, and a configuration that assembles a pipeline from a file. A stage that cannot handle a document must pass it on rather than fail, and a stage that genuinely cannot proceed must fail with a message that names the stage.
3. User requirements¶
- [ ] Convert a file from any supported input format to any supported output format.
- [ ] Choose the stages in a configuration file, including none.
- [ ] See each stage's effect: a
--tracemode printing the document between stages. - [ ] Get a failure message naming the stage, the input file and the line, where applicable.
- [ ] Add a new stage or format without editing the pipeline runner.
- [ ] Convert a directory of files in one run, reporting per-file results.
4. Functional requirements¶
- [ ] A
Documentmodel: sections, paragraphs, inline emphasis, and tables if you support them. - [ ] A
Readerabstraction with at least two implementations, chosen by file extension or configuration. - [ ] A
Stageabstraction with at least four implementations, including one that can decline a document entirely. - [ ] A
Writerabstraction with at least two implementations. - [ ] A
Pipelineowning ordered stages, running a document through them. - [ ] A configuration loader assembling readers, stages and writers by name.
- [ ] Typed errors: unknown format or stage, malformed input with a line number, and a stage failure naming the stage.
5. Non-functional requirements¶
- [ ] The pipeline runner contains no knowledge of any specific stage, reader or writer.
- [ ] A failing stage is reported with its name and leaves no partial output file behind.
- [ ] Stages are independent: reordering them in the configuration never crashes, even if the result is odd.
- [ ] Ownership is explicit; documents are moved between stages rather than copied, or the copy cost is measured and reported.
- [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror.
6. Suggested features¶
- A stage that collects statistics about the document and passes it through unchanged.
- Templates with placeholders filled from a small key/value file.
- A
--dry-runmode listing the stages that would run, in order. - A second output format for the trace, so traces can be diffed between runs.
7. OOP concepts involved¶
| Concept | Required | Where it appears |
|---|---|---|
| Multiple classes | yes | Document model, readers, stages, writers, pipeline, configuration, error types |
| Relationships | yes | Pipeline ◆ stages; stage ──> document; writer ◆ output stream; reader ┈┈> document factory |
| Encapsulation | yes | The document model's invariants hold after every stage; no stage pokes at another's internals |
| Inheritance or composition | yes | Interfaces for the three extension points; the pipeline composes stages and owns none of them |
| Polymorphism | yes | Readers, stages and writers all dispatch; the runner names none of them |
| The STL | yes | std::vector for stage order, std::map for registries, <sstream> for parsing and formatting |
| Exception handling | yes | Unknown names, malformed input and stage failures are distinct types with context |
8. Suggested design considerations¶
The pattern decision you must make and justify¶
Chain of responsibility, for the stages. A stage usually transforms and passes on, but a stage that cannot handle the document must pass it through rather than crash — that is the chain. The alternative is a strategy: every stage must handle every document, returning the document unchanged when it has nothing to do. Both work; you must argue which requirement settled it for you.
Abstract factory, for readers and writers. Format selection must produce a family of related
objects (reader, writer, and possibly a format-specific stage set) without the configuration
loader naming concrete classes. The alternative is a registry of std::function factories —
lighter, and arguably enough. Choose, and decline the other with a reason: that is the
declined-pattern item.
Ideas worth weighing and recording: decorator, if a stage is really a wrapper around an output (worth considering and probably not earned); memento, if you want stage-level undo (not earned unless you add a preview mode).
Other design points¶
The document model is the contract. Stages compose only because they agree on one model. If a stage needs private access to another's data, the model is missing a concept — not the stage missing a friend declaration.
Partial output is a real hazard. If stage three of five fails after writing a line, the archive has a corrupt file. Write to a temporary and rename on success; test the failure path.
Trace mode pays for itself twice. It makes the pipeline observable during development, and it makes the tests readable: input, trace, output, all as text.
9. Minimum viable version¶
One reader, two stages (normalise whitespace, reflow), one writer, a pipeline that runs them in
order, and a small main converting one file. Errors named, but no configuration file.
10. Intermediate extension¶
Add the third format, the configuration file, and two more stages including one that declines a document. Then verify that a pipeline with the declining stage in the middle runs to completion and that a genuinely failing stage leaves no output file.
11. Advanced extension¶
Add directory conversion with per-file reporting, the trace mode and the statistics stage. Then write the design note: the two patterns, the declined alternative, and the decision you would reverse — with the cost. Include a real trace as evidence.
12. Testing checklist¶
- [ ] Each reader produces the documented document model from a small sample
- [ ] Each writer produces byte-identical output for the same document, run twice
- [ ] A round trip through a lossless pair of formats preserves the document
- [ ] A stage that declines a document passes it through unchanged (compared byte-for-byte)
- [ ] A stage that fails reports its own name and the input line
- [ ] A failing stage leaves no partial output file on disk
- [ ] An unknown stage or format in the configuration is refused with the name in the message
- [ ] Reordering the stages never crashes, for every permutation of four stages
- [ ] A pipeline with zero stages reproduces the input document at the output
- [ ] Converting a directory reports one line per file, and continues past a bad file
- [ ] Trace output between stages matches the expected document at each step
13. Documentation checklist¶
- [ ] The document model, drawn as a small class diagram
- [ ] The three extension points and how to add to each, in a short "how to extend" section
- [ ] The configuration format with a full example
- [ ] The two patterns, the requirement each serves, and the declined alternative
- [ ] A real trace, pasted, showing the document between two stages
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| Extension points | New reader, stage or writer needs no change to the runner | 30% |
| Pattern decisions | Two patterns justified; the declined alternative argued with a requirement | 20% |
| Model quality | Stages compose through one model with no back doors | 15% |
| Failure handling | Named-stage failures, no partial output, per-file reporting | 15% |
| Testability | Trace mode plus byte-level round-trip tests | 10% |
| Documentation | Model, configuration and extension guide written down | 10% |
Related: Advanced projects · Project roadmap · All projects · Module 15 — SOLID and Design Patterns