What is the Account class doing now?
What are its responsibilities?
By only looking at its state that its properties declared in it,
we can tell that the Account is managing Balance,
but also managing Closed and Verified states.
I don't like these state-related properties.
Why not move them where Freezable state is implemented?
There's nothing that prevents me from defining even more states,
like Closed or NotVerified.
These new states would operate in exactly the same way as the Frozen
and Active states that we have implemented already.
Without further ado, I will rename this interface.
From now on, it will be known as the AccountState interface.
Methods of the Account that are affecting the state are Deposit,
Withdraw, and Freeze, methods that we already see in the interface,
but also HolderVerified, and Close.
I will swiftly add these two methods to the State interface.
Now I can define a new state and name it NotVerified.
This state will be able to handle most of the calls in specific ways.
Another state will be Closed.
This completes the list of classes we will have in the end.
Now the implementation.
We can start with a Closed state, for instance.
Since Closed account doesn't allow any operations on it,
I will simply return this reference from all methods.
Closed account will remain closed forever,
no matter which methods a client may call on it.
This was more than easy, right?
Remember how all matters of the AccountState used just to
return when the Account was closed.
Well, returning this reference from the State object,
is the equivalent to just exiting the method of the Account class.
We can turn to the NotVerified state then.
When a client asks the Account to close itself,
this state should respond by turning into the Closed state.
It will signal that by returning the Closed object
when its Close method is called.
Deposit method will be somewhat more engaged.
Current implementation of the Account class is leaving the
account in the same non-verified state,
but it is allowing the money to be deposited.
Now this is the problem.
How is the Account object supposed to know whether to change the balance or not?
This is the point at which we would remember one technique which
is not used that often in modern programming,
callbacks.
If you know what I mean,
you will probably have a feeling like callbacks are so 1980s.
Let me explain what is the purpose of callbacks.
Suppose that we have two operations, like operationA and operationB.
And operationB must strictly follow operationA.
Whenever A is executed, B must be executed next.
How can be write code that guarantees that.
One way to accomplish this is to pass B as a callback to A.
Now A executes its body, whatever it is,
and then A invokes B just before exiting.
Think about this at your leisure.
I have an impression that this technique is rarely
used in object-oriented design today,
and I think that it should receive more attention.
So much more attention,
that I will apply it right in the middle of this AccountState interface.
I will change the signature of the Deposit method and make it receive an Action.
This Action will be invoked when the situation is
right to add money to the balance.
Now back to the NotVerified state.
Requirements say that the Deposit in the NotVerified state should be accepted,
but Account should remain non-verified.
This is how it goes.
That is all.
You can see from this example how the callback pattern
helps ensure the flow of operations.
We will see more of it in other state classes.
Now the Freeze method.
Let's say that the requirements are specifying that
NotVerified account remains non-verified,
even if someone asks to freeze it.
Done.
And now the HolderVerified method.
This method is the whole purpose why this entire
NotVerified state was introduced.
The method should return the Active state and in that way signal
that the Account holder has been verified.
And when said that way,
it becomes obvious that NotVerified state should also keep
record of the OnUnfreeze callback function.
It obviously has to know about that function in order
to pass it further to its successor.
This will do it.
And the last method is Withdraw.
Just for completeness, I will add a callback to the Withdraw as well.
This function is supposed to be invoked after everything was done that has
to precede balance update when money is being withdrawn.
And now the twist.
Since requirements are stating that money cannot be withdrawn
before Account holder has been verified,
Withdraw method in the NotVerified state will just do nothing,
plain nothing.
It will not even attempt to call the callback function.
This is the result of joint efforts of abstract interface,
explicit state class, and the callback pattern.
They have all gathered around the problem of withdrawing
money from a non-verified account, and they have reached the perfect solution,
to ignore the call.
Other states will not ignore it, though.
And that is the beauty of explicit states.
Each state can choose implementation that fits it most.
Now we can proceed a bit faster form this point on.
This is complete implementation of the Frozen state.
Its Deposit and Withdraw methods will receive corresponding callback functions
as specified in the interface this class is implementing.
In this class, callback functions will finally see their full use.
Both in the Deposit and in the Withdraw,
we have to call the supplied function after unfreezing the account.
Now we can see why I think that the callback was the
right solution for the problem.
This implementation of the Frozen state is following the
client's requirements to the letter,
and this is the final implementation of the Active state.
Once again,
we see that callback functions are invoked
unconditionally in Deposit and Withdraw methods.
When I said unconditionally,
that reminds me to say that now with all states of the
Account split into separate classes, everything is happening unconditionally.
There will be no more branching logic in the Account class.
Type of the state object is playing the role of the branching instruction now.
If we have an object of one type, we will invoke one method at runtime.
If we have an object of another type, then another method will be invoked.
Everything is the same as branching,
only no explicit branching exists anywhere any more.
That is how we can leverage dynamic dispatch to
substitute branching instructions.
By placing one object or the other at runtime,
we can decide to execute one method or the other.
Everything is the same as with explicit branching instructions,
only this time the object-oriented compiler is doing the heavy lifting for us.
And all that is yet another benefit of state objects.
Now in the end, it only remains to fix the Account class.
First of all, IsVerified and IsClosed flags will have to leave.
Next, this property will change its name and become State.
Further down the stream, initial state of the Account will be NotVerified.
More things follow.
Deposit method will not think about whether the
Account is closed or not anymore,
but it will have to turn Balance manipulation into a callback.
And so we'll go with the Withdraw method.
It will not think whether the Account holder has been verified,
and it will not think about whether the account has been closed.
But it will turn its balance changing logic into a callback as well.
Verification will just be delegated to the contained state.
And so will be the closing feature.
Did I promise that the Account class will become simple again?
Well here it is, the simple Account class.
All this class is doing is dealing with the balance.
Everything else was removed from the class and closed in
corresponding AccountState classes.
This is what you get when you replace branching logic with explicit states.
What you can do with the states is to move them,
and move their changing logic into separate classes.
In that way,
we can greatly simplify the class which used to contain
all that logic mixed in one place.