在Python中使用命令模式实现撤销/重做
我看到使用命令模式是实现撤销/重做功能的一个很流行的方法。实际上,我发现可以把一系列操作堆叠起来,然后按顺序反转它们,以达到某个特定的状态。不过,我不太确定在Python中该怎么做,而且我看过的大多数教程只是讲了一些概念,并没有展示如何在Python中实际实现。
有没有人知道在Python中撤销/重做功能是怎么工作的?
作为参考,这是我的(可能很幼稚并且有很多错误的)代码:
# 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)
def execute_undrawing(self):
self.draw.unexecute(self.point1, self.point2)
# invoker
class InvokeDrawALine:
def command(self, command):
self.command = command
def click_to_draw(self):
self.command.execute_drawing()
def undo(self):
self.command.execute_undrawing()
# receiver
class DrawALine:
def execute(self, point1, point2):
print("Draw a line from {} to {}".format(point1, point2))
def unexecute(self, point1, point2):
print("Erase a line from {} to {}".format(point1, point2))
实例化如下:
invoke_draw = InvokeDrawALine()
draw_a_line = DrawALine()
draw_command = DrawCommand(draw_a_line, 1, 2)
invoke_draw.command(draw_command)
invoke_draw.click_to_draw()
invoke_draw.undo()
输出:
Draw a line from 1 to 2
Erase a line from 1 to 2
显然,这个测试并没有允许堆叠多个操作来撤销。也许我完全搞错了,所以希望能得到一些帮助。
2 个回答
2
我会这样做
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)
一些想法:
- 想要撤销一个操作可能会很困难。比如说,你怎么撤销一条线呢?你需要找回那条线下面原本的内容。通常更简单的方法是回到一个干净的状态,然后重新执行所有的命令。
- 每个命令应该放在一个单独的对象里。这个对象应该保存执行这个命令所需的所有数据。
- 在Python中,你不需要定义一个命令类。我这样做是为了说明我希望命令对象实现哪些方法。
- 当你撤销操作时,重新应用所有命令可能会导致速度问题。优化这个过程留给读者自己去思考。
2
这里有一个实现方法,它把命令保存在一个列表里。
# 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)