Idle
state waiting for someone to Insert Bread
, which will make it transition into its Bread Inserted
state. The device will remain in this state until its lever is pulled (Pull Lever
transition), which forces it into its Toasting
state. After a certain amount of time the bread is ejected (Eject Bread
transition) and the toaster transitions into its BreadEjected
state. Finally, the bread is removed (Remove Bread
transition) and the device returns into its initial Idle
state again.Idle
state into the Toasting
state without going through the Bread Inserted
state.ToasterOperations
inside the Toaster
class.Toaster
class, we would likely end up with a lot of opaque and incoherent spaghetti code. After all, the exact behavior triggered in each method entirely depends on the current state of the Toaster
.if... then... else
statements and according state logic. Instead, we will delegate the action logic that is to take place when a transition is called to the current Toaster's state itself. To do so, we will have to define different state classes (essentially encapsulating our behaviors using the state pattern) and have them implement our ToasterOperations
interface.ToasterState
that other state classes will inherit from. Every distinct state is implemented in its own State
class. The base implementation makes sure that any method called on these causes an Error
to be thrown. After all, we don't want our Toaster
to change state outside of our well-defined transitions.State
the toaster transitions into.Idle
state we will implement the IdleState
class and have it override our insertBread()
method. When bread is inserted into the toaster, we will force it to transition into the Bread Inserted
state, which is implemented in the BreadInsertedState
classToaster
object to change its internal state. This will be done by referencing the current state the toaster is in through a local variable. A call to any of our transition methods on the Toaster
object is then delegated to this current ToasterState
instance.Toaster
class as follows:Toaster
logic that each call to one of the transition methods will set the reference of the local _state
variable to a new instance. Unless, of course, the transition called is currently not allowed, in which case an Error
is thrown as per our ToasterState
base class.Toaster
class. This task, though, I will happily leave to the reader as an exercise.