Using the Command Pattern to Undo and Redo in ArrayLists

So, I have a program in which you can log in and add / remove friends to and from friends arraylist. I may also like a certain thing, and this thing will be stored in likes arraylist. I will be asked to make undo and redo options for whatever I do.

So, I want to add apple as a friend. After that, when I choose the undo option, I can undo this action, so apple will not be my friend. How can I approach this with the Command Pattern when the input is any name or word entered for storage in friends arraylist?

I did some research and found that using the team template might be my best bet, as it should be done within the Facebook class that I already have. I guess I will have to use two different stacks, but I'm a little lost in the subject.

I decided to add parts of what I have in order to get a little more information about what I need to do and what my program does.

In the driver program

 Facebook facebook1 = new Facebook(); if (userInput == 6) { System.out.println("Login"); String operand1 = getOperand("What is the Username? "); String operand2 = getOperand("What is the Password? "); System.out.println("Enter a friend to be added. "); String operand3 = getOperand("What is the Username? "); facebook1.friend(operand3); } if (userInput == 7) { System.out.println("Login"); String operand1 = getOperand("What is the Username? "); String operand2 = getOperand("What is the Password? "); System.out.println("Enter a friend to be removed. "); String operand3 = getOperand("What is the Username? "); facebook1.defriend(operand3); } if (userInput == 12) { System.out.println("Login"); String operand1 = getOperand("What is the Password? "); facebook1.undo(); } if (userInput == 13) { System.out.println("Login"); String operand1 = getOperand("What is the Password? "); facebook1.redo(); } 

In Facebook class

 ArrayList<FacebookUser> recommendedFriends = new ArrayList<FacebookUser>(); void friend(String newFriend) { boolean positiveChecker = false; for (int i = 0; i < recommendedFriends.size(); i++) { if (recommendedFriends.get(i).toString().equalsIgnoreCase(newFriend)) { System.out.println("Error: This friend already exists."); positiveChecker = true; } } if (positiveChecker == false) { FacebookUser friend = new FacebookUser(newFriend, newFriend ); recommendedFriends.add(friend); System.out.println(friend + " is now your friend."); } positiveChecker = false; } void defriend(String formerFriend) { boolean positiveChecker = false; for (int i = 0; i < recommendedFriends.size(); i++) { if (recommendedFriends.get(i).toString().equalsIgnoreCase(formerFriend)) { recommendedFriends.remove(i); System.out.println(formerFriend + " has been removed from your friends list."); positiveChecker = true; } if (recommendedFriends.size() == (i + 1) && recommendedFriends.get(i).toString() != formerFriend && positiveChecker == false) { System.out.println("Error: There is no friend with this username."); } } positiveChecker = false; } public interface Command { public void undo(); public void redo(); } 
+6
source share
2 answers

When you cancel 2 things, you take a completely new action, you need to โ€œforgetโ€ the history of the replay and replace it with a new command, right?

For instance...

  • Add as friend Jim
  • Add Friend Bill
  • Add Friend Jill
  • Remove Jim
  • Cancel
  • Cancel

The state should be "Jim" and "Bill."

So, you really need only one list and a pointer to the current "command", for example ...

 // Note: NOT thread safe! public class CommandStack { private List<Command> commands = Collections.emptyList(); private int nextPointer = 0; public void doCommand(Command command) { List<Command> newList = new ArrayList<>(nextPointer + 1) for(int k = 0; k < nextPointer; k++) { newList.add(commands.get(k)); } newList.add(command); commands = newList; nextPointer++; // Do the command here, or return it to whatever called this to be done, or maybe it has already been done by now or something // (I can only guess on what your code currently looks like...) command.execute(); } public boolean canUndo() { return nextPointer > 0; } public void undo() { if(canUndo()) { nextPointer--; Command commandToUndo = commands.get(nextPointer); // Undo the command, or return it to whatever called this to be undone, or something command.undo(); } else { throw new IllegalStateExcpetion("Cannot undo"); } } public boolean canRedo() { return nextPointer < commands.size(); } public void redo() { if(canRedo()) { commandToDo = commands.get(nextPointer); nextPointer++; // Do the command, or return it to whatever called this to be re-done, or something commandToDo.execute(); } else { throw new IllegalStateException("Cannot redo"); } } } 

If I...

 interface Command { /* execute / undo etc */ } public class AddFriendCommand implements Command { private String friendName; // ... other fields, constructor / getters etc ... public void execute() { // Actually do it... System.out.println("Added friend " + name); } public void undo() { // Undo it... System.out.println("Removed friend " + name); } } public class RemoveFriendCommand implements Command { private String friendName; // ... other fields, constructor / getters etc ... public void execute() { // Actually do it, maybe throw exception if friend does not exist? // (that would have to be a runtime exception unless you want the interface method to throw stuff); System.out.println("Removed friend " + name); } public void undo() { // Undo it... System.out.println("Added friend " + name); } } 

You can repeat the above sequence using ...

 CommandStack stack = new CommandStack(); stack.doCommand(new AddFriendCommand("Jim")); stack.doCommand(new AddFriendCommand("Bill")); stack.doCommand(new AddFriendCommand("Jill")); stack.doCommand(new RemoveFreindCommand("Jim")); stack.undo(); stack.undo(); 

If you have now made a new command (via doCommand), it will forget that you have ever added โ€œJillโ€ or deleted โ€œJimโ€, but instead will now remember the new command and the rest of the command history, which has not been canceled.

Hope this helps.

+3
source

You misunderstand how a team template works. You want to have a separate List your Commands , where each instance of the command represents an action .

So you would like to have something like:

 List<Command> actionStack; 

and then enter the material e.g.

 public class AddCommand implements Command { private final void List<FacebookUser> userList; private final void FacebookUser newUser; public AddCommand(List<FacebookUser> userList, FacebookUser newUser) { this.userList = userList; this.newUser = newUser; } @Override public void undo() { userList.remove(newUser); } @Override public void redo() { userList.add(newUser); } } 
0
source

Source: https://habr.com/ru/post/985216/


All Articles