- [Instructor] In the last two lessons
we introduced the command pattern
and we even coded a few command objects.
We also coded a conductor.
And the conductor, at present,
has a run method to run any of our command objects.
So in this lesson, we're actually going to show the power
of the command pattern, by improving the conductor.
Now I'm inside my exercise files for chapter four,
chapter four lesson five, which contains the files
that we left off with in the last lesson.
Now since all of the commands are run through the conductor,
we can actually do a bunch
of awesome stuff with the conductor.
We can cue up commands, we can actually make sure that only
a certain amount of commands are running each time,
and another thing we can do is implement the history
of all of the commands that we've run.
Let's go ahead and do that.
I'm going to go ahead and add a constructor to my conductor
and we're going to add this.history
as a field of the conductor.
So we now have an array that we can save the commands.
I'm going to go ahead and simply
add a this.history.push command,
to push the command into this array,
right after we execute the command on line nine.
So once we've run a command,
we'll put it into the history array.
This will allow me to print the history by adding
a print history method and I can say this.history for each.
So for each command that we have in history,
we can console.log that command's name.
And this will print a history of all of the commands
that we've run in the terminal.
I'm actually not done with printing the history,
there's some other cool stuff we can do.
If we have a history, we can actually undo
and redo that history as well.
I'm going to go ahead and add another array
for any commands that we might want to undo.
We're going to save them in this array, undone.
So this is going to be a list of undone commands.
So the conductor, we should be able to add an undo method.
And what I want to do inside of my undo method here,
is I'm going to create a variable for the command
that we're going to undo and I can grab that from history.
This.history.pop, we'll go ahead and pull out
the last command and then, so long as the command implements
an undo interface, we should be able to undo it.
So we're going to make that a requirement
for any undoable commands,
I'm not actually going to do a check for that for brevity.
But let's just call command undo.
And then we'll go ahead and add
this command to the undone array.
So when we undo a command, we pull it from history,
we invoke undo, and then we add it to the undone array.
So let me go into my commands.js
and the exit command we cannot undo.
When we actually execute an exit, we leave the application.
But we can undo the create command,
and we want to require that we actually have an undo method.
Since when we call undo with the conductor,
our command needs to implement an undo method.
So in order to undo a create file,
we need to delete the file.
So I'm going to come up here to line one and I'm also
going to import the unlink method from the file system.
This method can be used to remove a file.
So coming back down here to undo,
I'm simply going to run an unlink and this.fullpath
and I'm also going to add another dummy function here,
just a placeholder for the callback.
Again we're going to handle these callback functions
in a later lesson.
So we'll go ahead and save our commands.js.
And let's go over to the conductor and do one more task.
Let's go ahead and implement a redo as well.
So under the undo, I'm going to implement a redo method.
And the redo is going to be, pretty much,
the opposite of the undo.
We're going to grab the command from this.undone.
So if we want to do a redo, we have to grab
whatever command was last undone.
And then we're going to go ahead and re-execute the command.
So that is redoing a command.
And since we've executed this command again,
we want to say this.history.push command,
because we've redone it.
Now the last thing we need to do is just add some logs,
so that our users know that we're undoing
and redoing commands, so we will add a console.log here
and call it undo and then we'll put the command itself,
command.name so we know that we're doing that.
And I'll go ahead and copy this console log
and also add it to line 27, right after we pop
the command from the undone array and we'll say redo.
So if our conductor keeps track
of the commands that we've run,
as well as the commands that we've undone,
we can easily implement undo and redo functionality.
So let's go over to the index.js
and we have a few more actions that users can take,
so on line 10, I want to notify them
that they can also check their history by typing history.
They can undo an event by typing undo,
or they can redo by typing redo.
So we're letting our users know
what is available to them through this interface
and then we need to add these events too.
So for history, when our users type that,
we're simply going to use the conductor.print history method.
Then we'll simply use the undo method on the conductor.
And the same thing for redo, so if we need to redo any
undone commands, we can go ahead and say conductor redo.
There we go.
So now we've added some new functionality
by adding those methods to our conductor
and then running them when the user requests that command.
So let's go back over to our terminal
and run this application.
And we'll create a few files.
So I'll go ahead and create a helloworld.txt hello world
and we'll create a hiworld.txt hi world
and let's create one more.
We'll create yoworld.txt and we'll say yo world.
Now if I wanted to see what commands I've executed,
I can type history and we can see
that we've created hello world,
we've created hi world, and we've created yo world.
In fact, if we come over to our files,
we should see hi world, hello world, and yo world
are now newly created text files within the start folder.
So let's go ahead and see what happens when we undo.
If I call undo, it says undoing create yo world.
I go back over to the folder, yo world has been removed,
we don't see it anymore.
So I'll go ahead and undo again.
So now we've undone creating hi world.
Go over to the folder, we no longer see a hi world.
If I want to redo a command, I can simply type redo.
And now we have redone creating hi world,
it's back in the folder and we can go as far
as to redo one more time,
to redo creating yo world back inside of the folder.
So as we can see, the command pattern is pretty powerful.
If we take a look at the conductor,
the conductor can save a history of the commands,
as well as easily let us undo or redo
commands through this one class.