Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Command pattern for undo and redo in ArrayLists

So I have a program where you can log in and add/remove friends to and from the friends arraylist. Also I can like a certain thing and that thing will be stored into the likes arraylist. I'm asked to make undo and redo options for whichever action I do.

So I want to add apple as a friend. After that when I select the undo option, I can undo that action so apple wouldn't be my friend. How I can approach this with a Command Pattern when the input is whatever name or word I inputted to store into the friends arraylist?

I did some research and found that using a command pattern could be my best bet since this has to be done under the Facebook Class I already have. I'm assuming I'll have to use two different stacks, but I'm getting a bit lost in the topic.

I decided to add parts of what I have so that I can get a bit more help on 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 the 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();
    }
like image 355
Ultimania Avatar asked Apr 15 '15 20:04

Ultimania


People also ask

Which of the following pattern supports undo and redo operation?

There are two main software development patterns that are the obvious choices when dealing with operations like undo and redo: Memento and Command patterns.

How do you implement undo and redo in Java?

If “WRITE” string is encountered, push the character to Undo stack. If “UNDO” string is encountered, pop the top element from Undo stack and push it to Redo stack. If “REDO” string is encountered, pop the top element of Redo stack and push it into the Undo stack.

How do you implement undo and redo in C++?

One way to implement a basic undo/redo feature is to use both the memento and command design patterns. Memento aims at keeping the state of an object to be restored later for example. This memento should be as small as possible in an optimisation purpose.


1 Answers

When you undo 2 things then do a completely new action, you need to "forget" the "redo history" and replace it with the new command, right?

For example...

  1. Add Friend Jim
  2. Add Friend Bill
  3. Add Friend Jill
  4. Remove Jim
  5. Undo
  6. Undo

State should be "Jim" and "Bill".

So you only really need 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 had...

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's method to throw stuff);
        System.out.println("Removed friend " + name);
    }

    public void undo() {
        // Undo it...
        System.out.println("Added friend " + name);
    }
}

You could repeat the sequence above 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 now did a new command (via doCommand) it would forget that you ever added "Jill" or removed "Jim", but instead would now remember the new command and the rest of the command history that was not undone.

Hope this helps.

like image 129
BretC Avatar answered Oct 20 '22 09:10

BretC