When you want to add multiple functionality to a class/object and you want to have flexibility to add them whenever you need, Decorator comes handy. you can extend the base class and add your desired changes but this way you come up with lot of sub-classes that could blow your mind. but with decorator you can have your desired changes but still have a simple, understanding flow.
your design is easily open for extension but close to modification with really simple logic.
The best example could be the Stream objects that are implemented in Java and C#.
for example you have a File Stream and in one use-case You want to Encrypt that, then Zip that, then Log that and finally do something fancy with it. then in another class you decide to do something else You want to Convert that, then Encrypt that, then get the timing, bluh, bluh, bluh. again you have another flow in other classes.
if you want to use Inheritance you have to create at least 3 sub-classes and if any other requirement is needed you have to add more sub-classes that in this case (Stream) you will end up dozens of sub-classes for small changes.
class EncryptZipLogStream{}
class ConvertEncryptTimeStream{}
class MoreStreamProcess{}
class OtherMoreStreamProcess{}
...
And in each use-case you have to remember what class is needed and try to use it.
But imagine you have used Composition rather than Inheritance and you have Decorator classes for each Stream process, you can easily combine your desired wrappers and have any desired processes with least amount of effort and max simplicity.
class WhereIWannaUseUseCaseOne {
EncryptStream(ZipStream(LogStream(FileStream("file name)))));
// rest of the code to use the combines streams.
}
then you come up with another use-case:
class WhereIWannaUseUseCaseSecond {
ConvertStream(TimeStream(LogStream(FileStream("file name)))));
// rest of the code to use the combines streams.
}
And so on and so forth, you have the flexibility to do what ever you want at run-time with a simple flow and understandable logic.