0:00 / 0:00 As I mentioned in the previous clip, one of the advantages of using the command pattern is that you
can implement an undo operation.
So that’s exactly what we’re going to do.
So given that a bank account experiences just deposits and withdrawals, I can postulate that these
two operations are the opposite of one another and I can add another interface member to I command,
which is going to undo a particular command.
So I’m going to have void undo like this.
Now, of course I have to implement this in the bank account command.
So let’s do this.
So here what I’m going to do is I’m going to once again switch.
So I’m going to switch on the action that was performed and I’m going to try and perform the opposite
action.
So at the moment, my assumption is that deposit and withdrawal sort of cancel one another.
So if I want to undo a deposit, I just say account dot withdraw with a specific amount.
And similarly for the withdrawal.
So I say account dot deposit a certain amount and we’ll see how this goes.
So I’ll put a break in here.
We have undo functionality now, so if I want to reverse all the commands that were performed on the
bank account, what I can do is I can simply go through them in reverse order, starting with the last
and ending with the first.
So I can say for each var C in innumerable dot reverse where this is from system link and I do commands.
Now, in case you’re wondering why I’m not doing commands dot reverse like in all of link.
The reason is that reverse is also a part of list of the API and it reverses a list in place.
It’s a mutating operation, so the only way you can call the link operation, funnily enough, is not
by using an extension method, by using enumerable dot reverse.
That’s just how it goes.
So here we say C dot undo to undo the operations in reverse order and we can write again the bank account
state here and let’s actually execute this and see what we get.
As you can see, apart from the slight weirdness of us simulating the deposit and withdrawal rollback
process, the final balance is zero.
And that’s exactly what we want.
However, let me break this.
Let me deliberately mess this up for you.
Let’s suppose that here at this stage we try to withdraw $1,000.
Now, obviously, the second of the two commands, the withdrawal command isn’t going to allow us to
withdraw the money.
But if I execute the whole sequence, you can see that instead of reverting to zero, we revert it to
Now, the reason why this was possible is because we assumed that the deposit is the opposite of withdrawal
and we didn’t verify whether the operation succeeded.
So we deposited 100 and the balance is 100.
We tried to withdraw 1000 and failed.
So it’s not even present here.
But when we roll back, we deposit 1000 because we assume that we succeeded in withdrawing the 1000
in the first place.
So how can you deal with this?
Well, it’s actually rather simple.
What you can do is you can just add a Boolean flag here on the withdrawal indicating whether it succeeded
or not.
So we’ll have public bool withdrawal, something like that.
And then, of course, if we succeed, we return.
True.
Otherwise we return false.
And now what happens is when we perform the actual withdrawal, we can note of the fact whether this
actually succeeded or not.
And then subsequently we can sort of work with it, so to speak.
But of course, this requires you to store some information in the command about whether the command
actually succeeded.
So, for example, if we go into our command, so bank account command, you would now have to add some
sort of flag.
Private bill succeeded, something to that effect.
And then, of course, you would set it in the deposit and withdrawal.
So suddenly, in the case of the deposit, you say succeeded.
Uh, equals true because a deposit always succeeds.
But here you say succeeded equals, and then you call the withdrawal method.
And that way you know that the account that the command actually succeeded.
So when you come to undo the command, you say basically, if this command didn’t even succeed, why
do we bother?
If not succeeded, just return?
That’s pretty much it.
And now, of course, what we can do is we can once again try this whole thing, try to execute this
particular program.
And now the balance reverts back to 100.
Notice we’re not rolling back a transaction which failed.
So this kind of illustrates two things.
First of all, it illustrates that you can easily do an undo operation on a command or indeed a sequence
of commands provided you reverse them correctly.
It becomes more interesting when you want to undo a command that’s in the middle of a stream of commands
and you want to check their internal consistency and whatnot.
This is, you know, something that you would have to implement using whatever logic your bank or whatever
is requiring of you.
However, the other thing is this idea of storing additional information inside the command.
So here we are storing whether the command succeeded or not.
You could theoretically store the balance of the bank account before and after the transaction as well
just for additional checking later on.
And this is good because then you can audit the entire sequence.
You can run the sequence from start to finish.
And if the final balance, for example, looks a bit off to you, then you can figure out exactly which
command behaved in a bizarre way and you can sort of go back to it.
And in the real world, all of the financial systems operate on this kind of transactional model, and
they all keep records, they are all subject to audits and so on and so forth, because, well, money
is tricky business.
You don’t want to make any mistakes with money.
And of course, in the real world, we don’t operate necessarily on integers in terms of money.
We might use a special money type and all the rest of it.
So that’s something for a different course.
But this is essentially how you implement the undo operation in commands.
And similarly, you can do a redo operation as well as other kinds of manipulations as well.
Play Play Stop Play Start Play