Design Patterns – Does This Decorator Implementation Violate the Liskov Substitution Principle?

designdesign-patternsliskov-substitutionobject-orientedsolid

Please consider the following implementation of the Decorator design pattern:

WordBank objects store strings and return them to the client through the method getWords().

The decorator class, WordSorter, is a subclass of WordBank (as in the Decorator pattern). However it's implementation of getWords() sorts some of the strings and removes them, before returning the array to the client.

For example a WordSorter might delete all of the strings in the bank that start with letter 'a', and only after that return the array to the client.

Does this violate the Liskov Substitution Principle? Since some implementations of WordBank return strings while others first sort through the strings and only return some of them, I'm not sure if it's safe to say that a WordSorter can be used anywhere any other WordBank is used. Or am I understanding this principle wrong?

enter image description here

Best Answer

A WordSorter is-a WordBank, so code that works with a WordBank should also work when a WordSorter is used instead of a WorkBank. On the other hand, a WordSorter is-not-a SomeWordBank. The compiler won't even let you use a WordSorter in place of a SomeWordBank, so the issue does not even begin to arise.

There might be a LSP violation, but doesn't appear to be from the minimal specification you've given. Does WordSorter guarantee, for example, that one can add arbitrary strings to it and retrieve them all in the same order later? Then sorting the words would indeed break the that contract, and code that works for "correct" WordBanks can be broken by substituting a WordSorter. Whether there is such a guarantee is impossible to tell from the minimal UML diagram you've shown. If, for example, WordBank's contract says all words which are added are included in the result of getWords, then:

bank.add(w);
AssertContains(bank.getWords(), w);

should always work. That code would break if bank was a WordSorter, and it's WordSorter's fault for breaking the contract and hence violating the LSP. But if WordBank offers no such guarantee, then the above code is in the wrong (in the same way asser x*y == 42 will usually fail) and WordSorter is a perfectly well-behaved subclass.