a sublcass shouldn't alter the behavior of the parent?
That's a common misinterpretation of LSP. A subclass can alter the behavior of the parent, as long as it remains true to the parent type.
There is a good long explanation on Wikipedia, suggesting the things that will breach LSP:
... there are a number of behavioral conditions that the subtype must meet. These are detailed in a terminology resembling that of design by contract methodology, leading to some restrictions on how contracts can interact with inheritance:
- Preconditions cannot be strengthened in a subtype.
- Postconditions cannot be weakened in a subtype.
- Invariants of the supertype must be preserved in a subtype.
- History constraint (the "history rule"). Objects are regarded as being modifiable only through their methods (encapsulation). Since subtypes may introduce methods that are not present in the supertype, the introduction of these methods may allow state changes in the subtype that are not permissible in the supertype. The history constraint prohibits this. It was the novel element introduced by Liskov and Wing. A violation of this constraint can be exemplified by defining a MutablePoint as a subtype of an ImmutablePoint. This is a violation of the history constraint, because in the history of the Immutable point, the state is always the same after creation, so it cannot include the history of a MutablePoint in general. Fields added to the subtype may however be safely modified because they are not observable through the supertype methods. One may derive a CircleWithFixedCenterButMutableRadius from ImmutablePoint without violating LSP.
Personally, I find it easier simply to remember this: If I'm looking at a parameter in a method that has type A, would someone passing a subtype B cause me any surprises? If they would then there is a breach of LSP.
Is throwing an Exception a surprise? Not really. It's something that can happen at any time, whether I'm calling the Ship method on OrderState or Granted or Shipped. So I have to account for it and it's not really a breach of LSP.
That said, I do think there are better ways to handle this situation. If I were writing this in C#, I would use interfaces and check for the implementation of an interface before calling the method. For example, if the current OrderState doesn't implement IShippable, don't call a Ship method on it.
But then I also wouldn't use the State pattern for this particular situation. The State pattern is much more appropriate to the state of an application than to the state of a domain object like this.
So, in a nutshell, this is a poorly contrived example of the State Pattern and not a particularly good way to handle the state of an order. But it arguably doesn't breach LSP. And the State pattern, in and of itself, certainly doesn't.
cancel()
changes the state to cancelled.