An abstraction describes a concept, function, or process through its essential behavior while hiding the mechanisms behind it. It separates what clients need to know from how the work is performed, creating a precise contract rather than a vague one. section 5.0 section 5.1.3
Its purpose is to control complexity as a system evolves. When every new variation is added to the same class through more conditionals or copied blocks, that class becomes harder to change and test. An abstraction provides an extension point: new behavior can be introduced as another implementation instead of modifying established code. section 1.2.4 section 5.1
Discount rules
Start with direct code for one or two discount rules. As rules accumulate—such as seasonal discounts, quantity offers, and e-book promotions—repeated changes and long runs of if statements signal variability worth modeling. Define a small discount abstraction, then add each rule as a concrete implementation so rules can be plugged in, removed, or combined without changing the calling code. section 5.0 section 5.3.3 section 5.1.3
A useful abstraction has these properties:
- States a clear, lean contract: clients provide only necessary inputs and receive only what they need. section 5.1.3
- Hides implementation choices so clients can understand the flow without knowing concrete implementations. section 5.1.3
- Stays small, leaving nonessential work to concrete implementations. section 5.3
- Remains stable enough that clients can depend on it without frequent breaking changes. section 5.1.3 section 5.1.4
Do not abstract merely for elegance. Abstractions add indirection and make polymorphic flows harder to follow. Favor simple code when flexibility is uncertain, but introduce an abstraction when variation, composition, likely change, or hidden decisions justify its cost. Waiting for repeated pressure is sensible; acting early is also reasonable when future flexibility is clear. section 5.1.1 section 5.3.2 section 5.3.4
Infrastructure is a common boundary for abstraction. Isolate database, remote-service, caching, or file-storage details to limit the effect of change and support isolated tests. Do not pretend those details do not exist, however: over-hiding a relational database or remote call can obscure constraints developers need to design correctly. section 6.0 section 6.1.2
Use the decision guide to weigh flexibility against the extra complexity of an abstraction or extension point. section 5.3.1














