The decorator pattern is one that favours composition over inheritance [another OOP paradigm that is useful to learn about]
The main benefit of the decorator pattern - over subclassing is to allow more mix & match options. If you have, for instance, 10 different behaviours that a window can have, then this means - with subclassing - you need to create every different combination, which will also inevitably include a lot of code reuse.
However, what happens when you decide to add in a new behaviour?
With the decorator, you just add a new class which describes this behaviour, and that's it - the pattern allows you to effectively drop this in without any modification to the rest of the code.
With sub-classing, you've got a nightmare on your hands.
One question you asked was "how does subclassing change the parent class?" It's not that it changes the parent class; when it says an instance, it means any object you've 'instantiated' [if you're using Java or C#, for instance, by using the new
command]. What it's referring to is, when you add these changes to a class, you've got no choice for that change to be there, even if you don't actually need it.
Alternatively, you can put all his functionality into a single class with it turned on/off via flags... but this ends up with a single class that becomes larger and larger as your project grows.
It's not unusual to start your project this way, and refactor into a decorator pattern once you hit an effective critical mass.
An interesting point that should be made: you can add the same functionality multiple times; so you could, for instance, have a window with double, triple or whatever amount or borders as you require.
The major point of the pattern is to enable run-time changes: you may not know how you want the window to look until the program is running, and this allows you to easily modify it. Granted, this can be done via the subclassing, but not as nicely.
And finally, it allows for functionality to be added to classes that you may not be able to edit - for example in sealed/final classes, or ones provided from other APIs
Arguably they are supported in virtually every programming language.
What you need are "assertions".
These are easily coded as "if" statements:
if (!assertion) then AssertionFailure();
With this, you can write contracts by placing such assertions at the top of your code for input constraints; those at the return points are output constraints. You can even add invariants throughout your code (although aren't really part of "design by contract").
So I argue they aren't widespread because programmers are too lazy to code them, not because you can't do it.
You can make these a little more efficient in most languages by defining a compile-time boolean constant "checking" and revising the statements a bit:
if (checking & !Assertion) then AssertionFailure();
If you don't like the syntax, you can resort to various language abstraction techniques such as macros.
Some modern languages give you nice syntax for this, and that's what I think you mean by "modern language support". That's support, but its pretty thin.
What most of even the modern languages don't give you is "temporal" assertions (over arbitrary previous or following states [temporal operator "eventually"], which you need if you want to write really interesting contracts. IF statements won't help you here.
Best Answer
This is irrelevant. Has been for decades. You can remove it from the question, since it makes no sense with modern compilers to subvert their optimizers.
There is no downside to literate programming. (I expect dozens of -1 votes for that sentiment.) As a practitioner, I've never seen any problems.
There are some arguments against which all amount to "programming in a higher-level language is be subverted by tweaking the resulting code." Right. In the same way that programming in C++ is subverted by tweaking the
.o
file that gets produced. It's true, but irrelevant.Writing literate programs merely means combining high-level and detailed (code-level) design into one document, written with a suitable toolset that produces compiler-friendly files and people-friendly files.
When Knuth invented literate programming, mainstream OO languages didn't exist. Therefore a great deal of the original web and weave tools allowed him to create class-like definitions for abstract data types.
Much of that is irrelevant nowadays. Literate Programming tools can be quite simple if they're focused on modern, high-level object-oriented (or functional) programming languages. There is less need for elaborate workarounds because of the limitations of C (or Pascal or Assembler).
The approach to writing literate programs is no different from the approach to writing illiterate programs. It still requires careful design, unit testing, and neat coding. The only extra work is writing explanations in addition to writing code.
For this reason only -- the need to write coherent explanations -- literate programming is difficult for some programmers. There are a fair number of programmers who are successful (their code passes all the unit tests, is neat and easy to understand) but can't seem to write a coherent sentence in their native language. Don't know why this is true.
There are a very large number of programmers who appear to be only marginally successful and then only by accident. (There are enough bad questions in Stack Overflow that indicate that many programmers struggle to even grasp the fundamentals.) For programmers who ask largely incoherent stack overflow questions, we know they can't really do a good job of literate programming, because they can't do a good job of programming.