4

I am designing an order system and the state design pattern seems appropriate because the order can change its state and thereby the allowed functionalities for the order. Below is my basic class diagram:

I do not like this approach because clients cannot see if a method is supported and violates Liskov principle. I created an alternative below:

I like this better but still the client has to check if a method is supported. But they can still call the unsupported method and get an exception. Does this still violates the Liskov principle?

Is there a better design that is in compliance with Liskov and keeps the user from calling invalid methods for a specific state?

jaco0646
  • 15,303
  • 7
  • 59
  • 83
  • I don't understand what you mean by "cannot see if a method is supported". The concrete OrderStates will all implement the OrderState methods. Just some of them will raise an Exception when the state doesn't allow the operation currently. That doesn't violate LSP imo. Also, I don't see how this is different when instead of directly raising an UnsupportedOpEx, you first call one of the can* methods and then raise it. Imo, allowing any consumers of the Order to call the can* methods is worse, because it moves the responsibility outside the Order object. – Gordon Oct 17 '16 at 11:17
  • In class diagram 1 there is not way for the client to detect if an opperation is valid for a specific state. Then there is no way to know if a pay button or a cancel button should be displayed for. In my understanding of LSP throwing a notSupportedException is a common way of breaking the LSP. This because the subtype does not do what is says, Therefore the subtype cannot be substitutable for its basetype. – Roy van der Tuuk Oct 17 '16 at 11:28
  • The concrete `OrderStates` will all implement the `OrderState` interface and thus it's methods. All of these methods will - by interface contract if possible - potentially raise an Exception. So you can substitute any concrete subtype of the OrderState with another. Consequently, you don't violate Liskov's Substitution Principle. You can use the very same pattern to draw the buttons btw. Just pass a button renderer to the OrderState and have it return the correct button from the concrete OrderState, e.g. Confirmed will tell the ButtonRenderer to render a Pay button, etc. – Gordon Oct 17 '16 at 11:32
  • I looked up a definition of LSP "If S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substituted for objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.)" the correctness is violated because you are calling the pay method on an order and you expect you are paying the order. In stead the current state will never be able to pay. Thus this will corrupt the correctness of the system. – Roy van der Tuuk Oct 17 '16 at 11:36
  • See http://programmers.stackexchange.com/questions/181922/does-the-state-pattern-violate-liskov-substitution-principle, in particular http://programmers.stackexchange.com/a/181941/49178 - The correctness is not violated if the exception is to be expected. Otherwise, any method raising an exception would violate LSP. – Gordon Oct 17 '16 at 11:38
  • On a side note: I'd probably reduce the OrderState methods to just `advance()` – Gordon Oct 17 '16 at 11:46

1 Answers1

0

What you are showing is not State pattern. State pattern alters object behavior when its internal state changes. For example, a light switch can turn on or turn off the light when you toggle it, depending on its state (different behavior on the same method).

With this Order interface (4 diff methods) I don't see any benefits of introducing State pattern. It will only complicate things for no reason. But I don't know all the details so it is up to you what to do next.

Check this link to see examples of State patten implementation https://sourcemaking.com/design_patterns/state

Ihor Burlachenko
  • 4,689
  • 1
  • 26
  • 25
  • The reason why I am using the state pattern in the design is because the order changes state and this changes its behavior. If i create seperator order classes then the order can not change its behavior ar runtime. Do you have a suggestion for a different pattern? – Roy van der Tuuk Oct 17 '16 at 11:41
  • The simplest solution is a single Order model with 4 diff methods which throw an exception when you try to use a method when the object is in an inappropriate state. Usually, when I don't know all details I go with the simplest solution which will be easier to refactor later. But it all depends on the Order usage in your application/code. For example, if you had an Order interface with one `proceedToTheNextState` method you could create 4 states: Confirm, Cancel, Pay, Ship which do all the job related to the state and transit to the next state. – Ihor Burlachenko Oct 17 '16 at 11:56
  • Also, there might be many different reasons why you may want to split the Order behavior logic. And the resulting solution might not be the exact implementation of State pattern. It is all ok if it's all done for a reason. You can't break LSP if you follow your interfaces and method definitions in subclasses. – Ihor Burlachenko Oct 17 '16 at 11:57