Java Design Patterns – State Pattern vs Inheritance

cdatabase-designdesigndesign-patternsjava

In the following image for the State Pattern from Applying Domain-Driven Design and Patterns: With Examples in C# and .NET

enter image description here

I'm trying to persist the SalesOrder entity into the database. Normally I would have a salesOrder table in my database with a field currentState pointing to the current state of the entity.

This implementation is fine as there is a single entity and its states only contain different behavior.

However, what if I had to track other properties for the different states?
Examples:

  • When a SalesOrder is cancelled I need to track the cancelled quantity,cancellationReason, cancelledBy, cancellationDate.
  • When a SalesOrder is Refunded I need to track refundQuantity, refundPrice, refundFee.

So basically it is like I have a CancelledSalesOrder and RefundedSalesOrder entities with additional attributes. Same can be said for the other states as well.

So my problem now is that I will have to change the design from using the state pattern into using inheritance. However, inheritance doesn't allow my to dynamically change my state at run time (the problem which the state diagram solves initially).

so my questions:

  1. Does the state pattern fail when an entity in each state has additional attributes? what's the alternative?
  2. How should I persist my entities in this case? Single table inheritance? table per class? table per concrete class?
  3. If I have to maintain some attributes from a former state (Refund a Shipped order, but don't delete the Shipping address from Shipped state ), how would one approach this problem?
  4. How do you keep track of the order of which the states occurred?

Best Answer

No, the state pattern does not fail. Just make the additional "cancel" attributes part of the "Cancelled" state, and the additional "refunded" attributes part of the "Refunded" state ( you just forgot the latter state in your diagram, I guess?)

Since any "state" entity is an aggregate of the SalesOrder, any attributes of "state" can be seen just an "addition of details" to the SalesOrder.

Which kind of inheritance mapping you choose for your OrderState hierarchy actually does not matter for your problem, any of the typical three will work .

The state pattern also allows you to keep attributes of a former state even when the state changes afterwards. For example, you might need to keep the "shipping address" even when the state changes from "Shipped" to "Invoiced". This can be solved by allowing multiple states at once, just make the relationship between SalesOrder and OrderState "1:n" instead of "1:1". So you don't "change the state from shipped to invoiced" but add the additional state "invoiced" to an entity "StateOrder", without removing the state "invoiced" beforehand. Of course, using this model, when looking for all orders to be shipped, you must check not only for orders which are "granted", but for "orders which are granted but not already shipped". But that's only a detail in the processing which should not be too hard to handle.

Having multiple states is IMHO the "better" model, since it reflects the real world situation more adequate. For example, something you have shipped and invoiced is "shipped and invoiced", not "invoiced instead of shipped".