Do/Undo using command pattern in Python

后端 未结 2 554
北荒
北荒 2021-01-16 15:05

I have read that using command pattern is one of the most popular ways to accomplish do/undo functionality. In fact, I have seen that it\'s possible to stack a bunch of acti

相关标签:
2条回答
  • 2021-01-16 15:53

    Here is an implementation keeping the commands in a list.

    # command
    class DrawCommand:
        def __init__(self, draw, point1, point2):
            self.draw = draw
            self.point1 = point1
            self.point2 = point2
        def execute_drawing(self):
            self.draw.execute(self.point1, self.point2)
    # invoker
    class InvokeDrawLines:
        def __init__(self, data):
            self.commandlist = data
        def addcommand(self, command):
            self.commandlist.append(command)
        def draw(self):
            for cmd in self.commandlist:
                cmd.execute_drawing()
        def undocommand(self, command):
            self.commandlist.remove(command)
    
    # receiver
    class DrawALine:
        def execute(self, point1, point2):
            print("Draw a line from" , point1, point2)
    
    0 讨论(0)
  • 2021-01-16 15:57

    How I'd go about this

    class Command(object):
        def execute(self, canvas):
             raise NotImplementedError
    
    class DrawLineCommand(Command):
        def __init__(self, point1, point2):
            self._point1 = point1
            self._point2 = point2
    
        def execute(self, canvas):
            canvas.draw_line(self._point1, self._point2)
    
     class DrawCircleCommand(Command):
         def __init__(self, point, radius):
            self._point = point
            self._radius = radius
    
         def execute(self, canvas):
            canvas.draw_circle(self._point, self._radius)
    
    class UndoHistory(object):
        def __init__(self, canvas):
            self._commands = []
            self.canvas = canvas
    
        def command(self, command):
            self._commands.append(command)
            command.execute(self.canvas)
    
        def undo(self):
            self._commands.pop() # throw away last command
            self.canvas.clear()
            for command self._commands:
                command.execute(self.canvas)
    

    Some thoughts:

    1. Trying to undo an action can be hard. For example, how would you undraw a line? You'd need to recover what used to be under that line. A simpler approach is often to revert to a clean slate and then reapply all the commands.
    2. Each command should be contained in a single object. It should store all of the data neccesary for the command.
    3. In python you don't need to define the Command class. I do it to provide documentation for what methods I expect Command objects to implement.
    4. You may eventually get speed issues reapplying all the command for an undo. Optimization is left as an excersize for the reader.
    0 讨论(0)
提交回复
热议问题