Decorator
A FileDataSource with read and write ships fine, until someone wants compressed saves, then encrypted ones, then both. By the third option you're naming a CompressedEncryptedFileDataSource and the class names are multiplying faster than the features.
- ▸Three optional features by subclassing is eight classes; by wrapping it's three.
- ▸
new BufferedReader(new FileReader(f))is two wrappers around a file, you've stacked these for years. - ▸Swap the wrap order and the bytes change: compress-then-encrypt is not encrypt-then-compress.
You have a FileDataSource with read() and write(). It works. Then the requests arrive: save compressed to cut disk, save encrypted for the sensitive files, and some files need both.
The obvious move is to subclass:
class FileDataSource { read() {/*...*/} write(data) {/*...*/} } class CompressedFileDataSource extends FileDataSource {/*...*/} class EncryptedFileDataSource extends FileDataSource {/*...*/} class CompressedEncryptedFileDataSource extends FileDataSource {/*...*/}
Three features and you're already at the awkward combo class. Add buffering as a fourth and the honest count is sixteen: every feature is independently on or off, so the number of classes doubles with each one you add. Two features need four classes, three need eight.
Worse, the combo classes copy each other. CompressedEncryptedFileDataSource repeats the compression code from one sibling and the encryption code from another. Fix a bug in compression and you edit it in every class that compresses. The features are independent in your head but fused in the class tree, each combination frozen into its own name the moment you wrote it.