修改工厂函数中的不可变对象

时间:2012-04-23 03:36:36

标签: python design-patterns constructor immutability software-design

是否有描述以下设置的设计模式?这个设计是否有任何重大问题?

Widget实例可以由“哑”构造函数Widget.__init__()构建,也可以由“智能”工厂方法Workbench.upgrade_widget()构建:

class Widget:
  def __init__(self, abc, def, ...):
    self.abc = abc
    self.def = def
    ...
  ...

class Workbench:

  # widget factory function, which uses data from the workbench instance
  def upgrade_widget(self, widget, upgrade_info):
    widget = Widget(widget.abc, widget.def, ...)
    # I will modify the widget's attributes
    ...
    self.rearrange_widget(widget, xyz) # modifies widget's internal state
    ...
    widget.abc = ... # also modifies widget's state
    ...
    return widget

  # uses data from the workbench instance
  def rearrange_widget(self, widget, xyz):
    ...
  # this class does other stuff too
  ... 

小部件是不可变的,因为在完全初始化之后我不能修改它的实例(很多代码依赖于这个不变量)。但我发现在初始化时修改小部件非常方便,并使代码更清晰。

我主要担心的是我在另一个类中修改了“不可变”小部件。如果它只在upgrade_widget中,我可能会使用它,因为它不会修改传递给它的小部件。但是该方法依赖于其他Workbench方法(rearrange_widget),这些方法修改了它作为参数接收的窗口小部件。我觉得我无法控制这个“不可变”实例的实际修改位置 - 有人可能会意外地使用已经完全初始化的小部件调用rearrange_widget,从而导致灾难。

2 个答案:

答案 0 :(得分:1)

你现在如何强制执行Widget的不变性?

如果您向窗口小部件添加“锁定”属性,并将setattr包装以检查该属性,该怎么办:

class Widget(object):
    __locked = False
    def __init__(self,a,b,c,locked=True):
        ...
        self.__locked = locked

    def lock(self):
        self.__locked = True

    def is_locked(self):
        return self.__locked

    def __setattr___(self,*args,**kw):
        if self.__locked:
            raise Exception('immutable') # define your own rather than use Exception
        return super(Widget).__setattr__(self,*args,**kw)

然后在工厂:

class Workbench(object):
    def upgrade_widget(self,widget,upgrade_info):
        widget = Widget(widget.a,widget.b,...,locked=False)
        self.rearrange_widget(widget, blah)
        widget.c = 1337
        widget.lock()
        return widget

一般情况下,你可以非常肯定,一旦被锁定,这个课程就不会发生任何有趣的事情。任何关心窗口小部件不变性的方法也应该检查该窗口小部件的is_locked()。例如,rearrange_widget应该在执行任何操作之前检查窗口小部件是否已解锁。

尽管存在恶意篡改实例,但无论如何都会发生这种情况。它也不会阻止属性被自己的方法改变。

请注意,我上面写的代码(伪python)没有经过测试,但希望它能说明如何处理主要问题的一般概念。

哦,我不确定这种模式是否有特定名称。

答案 1 :(得分:1)

@chees:这样做的一种更简洁的方法是修改__dict__中的__init__,并使__setattr__始终引发异常(顺便说一下,{{1}不是一个好主意} - 它只是为了一般):

raise Exception

在Workbench中以相同的方式修改它(即使用class Widget: def __init__(self, args): self.__dict__['args'] = args def __setattr__(self, name, value): raise TypeError )不断提醒你,你正在做一些你不应该做的事情。