Invoice
class that works perfectly and has no bugs. It makes a PDF of an invoice. Then someone says they want an HTML invoice...
The example used in the quoted answer is hypothetical and somewhat imprecise:
- It is not clear if
Invoice
is an abstract business document that will generate a concrete representation (PDF, HTML, XML/EDIFACT electronic invoice ...) or if it is already a concrete representation (i.e. invoice it tightly coupled to the output, which the wording suggests).
- It is not either clear what is the "contact" of
Invoice
, i.e. the promises made by its interfaces.
Your doubts are therefore perfectly justified: if Invoice
promises a PDF output, you cannot derive an HTMLInvoice
output unless you break Liskov's substitution principle.
The whole problem here, is that the wording suggests that Invoice
is already coupled to its representation, which could be seen as an implementation detail. So, as you point out, it would not hide the implementation detail.
But this is a fictuous example and not real-world. First of all, the format of a business document is not an implementation detail: producing a self standing PDF which can be legally archived, or an HTM which allows hyperlink to additional information, or even an electronic invoice is nowadays often a requirement.
Keeping this in mind, you could think of a design in which Invoice
produces an output, leaving the format open. So Invoice
would be slightly more abstract than in the above mentioned example. If your invoice has a method ElectronicDocument ProduceOutput()
you could then have class specialisations PDFInvoice
, HTMLInvoice
, EDIFACTInvoide
that would return a PDF, and HTML or and XML/EDIFACT document provided those are all specialisations of ElectronicDocument
: no encapsulation issue; Open/Close and LSP are safeguarded.
But most probably, you should favor composition over inheritance here. The invoice format object would indeed better be defined at runtime. Perhaps it could change for an invoice, if the customer calls and tells that he wants it in another format (that's daily business in many accounting departments). Or perhaps even you could allow an Invoice
to work with several representations.
There are plenty of ways to decouple the invoice format from the invoice (e.g. Builder to construct different representations, output Strategy to select the right document generation algorithm, visitor if you'd prefer to have a document generator using the invoice rather than the other way round, etc...). So in reality, you'd first think about your design, and depending on your intents, you would then chose the most appropriate way to decouple elements and ensure proper separation of concerns.