2

我正在尝试编写一个简短的类来表示存储在远程服务器上的对象。我希望在本地设置对象的属性而不在远程服务器上设置它们通常是一个坏主意;这意味着本地对象不再代表远程对象,甚至没有意识到这一事实。

我曾计划使用如下代码来处理这个问题。如果你想设置它,它会让你在脚下开枪item.name,但假设用户会明智而不这样做。

class Item(object):
    def __init__(self):
        self.name = get_item_name()

    def set_name(self, name):
        try:
            set_item_name(name)
        except:
            handle_error()
            raise
        else:
            self.name = name

但是,我最近遇到了@property装饰器,它让我写了以下内容:

class Item(object):
    def __init__(self):
        self._name = get_item_name()

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        try:
            set_item_name(value)
        except:
            handle_error()
            raise
        else:
            self._name = value

这样做的好处是仍然允许使用 访问属性item.name,但也可以处理item.name = blah赋值。

然而,虽然第二个代码块提供(在我看来是)更好的行为,但它在属性设置中隐藏了一个函数调用,并且考虑到Zen的“显式优于隐式”以及 Python 类应该不向他们的用户隐藏东西,这是否会使第二个代码块不那么 Pythonic?

4

1 回答 1

3

用 OO 语言编写代码就是为了隐藏东西。当您编写一个方法doSomethingComplex()时,您基本上是在应用程序的语言中创建一个新词,它对必须使用该方法的开发人员隐藏了一些复杂的操作。

虽然显式通常比隐式更好,但这并不意味着总是。在您的情况下,问题不在于将方法调用隐藏在 setter 中,而在于您隐藏了网络。一旦您尝试对方法的用户隐藏真实的物理网络,您需要确保正确处理以下情况:

  1. 遥远的一方突然死亡
  2. 网络丢弃数据包(本地超时)
  3. 远程端抛出异常(您必须以某种方式将其获取到本地代码,但在本地可能没有意义)
  4. 远程端成功处理了呼叫,但网络丢弃了答案(所以你确实成功了,但你不知道)

无论你多么努力,这种隐藏的复杂性都会泄露出去。作为一个简单的例子,使用此代码的开发人员将不得不处理网络异常和超时(与任何其他方法相反)。

所以这里的休息不是你在 setter 中调用一个方法,而是你引入了一个全新的合约。

什么是更好的解决方案?使网络调用显式。这对代码的用户来说意味着更明显的努力,但他们总是知道他们要面对什么(而不是想知道为什么这个 setter 行为如此奇怪)。

建议的实现:使用命令模式来修改这些对象的状态。该命令需要修改对象的 ID(因此您不必保留本地副本)、要更改的字段和新值。

当远程端回复“成功”时更新本地缓存副本。如果远程端返回错误,则删除缓存的副本,以便再次获取它。

这将创建更多代码,但此代码将非常简单明了。当您尝试隐藏远程端时,您将拥有更少的代码,但它会更加复杂和晦涩。

PS:永远不要假设人们在很长一段时间内都是明智的。

于 2012-07-06T10:02:23.513 回答