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
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.