Python:如何使用必须引用其容器的元素设计容器

时间:2010-04-15 15:39:22

标签: python design-patterns oop

(这个头衔肯定不是那么好。请原谅我的英文,这是我能想到的最好的。)

我正在编写一个管理电子邮件域及其帐户的python脚本,而且我也是OOP设计的新手。我的两个(相关的?)问题是:

  1. Domain类必须做一些特殊的工作来添加和删除帐户,比如在底层实现中添加/删除它们
  2. 如何管理必须通过其容器的帐户的操作
  3. 要解决前一个问题,我会在Domain类中添加一个工厂方法,该方法将在该域中构建一个Account实例,并添加一个'remove'( anti-factory ?)方法处理删除。

    对于后者,这在我看来是“反oop”,因为逻辑上对帐户的操作(例如,更改密码)必须始终引用包含的域。在我看来,我必须添加一个返回到帐户的域的引用,并使用它来获取域类的数据(如域名)或调用方法。

    管理基础Vpopmail系统的代码示例(元素使用容器中的数据):

    class Account:
        def __init__(self, name, password, domain):
            self.name = name
            self.password = password
            self.domain = domain
        def set_password(self, password):
            os.system('vpasswd %s@%s %s' % (self.name, self.domain.name, password)
            self.password = password
    
    class Domain:
        def __init__(self, domain_name):
            self.name = domain_name
            self.accounts = {}
        def create_account(self, name, password):
            os.system('vadduser %s@%s %s' % (name, self.name, password))
            account = Account(name, password, self)
            self.accounts[name] = account
        def delete_account(self, name):
            os.system('vdeluser %s@%s' % (name, self.name))
            del self.accounts[name]
    

    另一种选择是Account.set_password调用一个可以完成实际工作的Domain方法 - 这听起来对我来说同样难看。

    另请注意重复数据(帐户名也作为dict键),听起来合乎逻辑(帐户名称是域内的“主键”)但帐户需要知道自己的名字。

    编辑:请注意上面的代码只是一个简单的例子,将其视为伪代码。它故意不关心错误条件或安全问题,并且在类的数据和方法(每个用户的垃圾邮件设置,自动响应程序,转发器,获取邮箱大小等等)方面不完整。

    此外,这是我手边的一个例子,但我认为它可以推广到其他不同的逻辑结构,类似于树,节点必须知道他们的孩子,孩子必须要打电话给父母(或上层祖先)去做的东西。 对我来说,这在逻辑上类似于类继承,但适用于彼此链接的不同类型(类)的实例。

4 个答案:

答案 0 :(得分:2)

对于您概述的操作,您根本不需要Account。它所拥有的唯一信息({1}}中尚未复制的是密码。您可以让Domain代替Domain.accounts

在你需要之前,不要将带有身份的类加倍。

对于一般情况下的价值,是的,当您拥有其他对象所拥有的对象时,向他们提供对其所有者的引用并让他们根据需要向上通信是很正常的。 Python没有一些语言为所有权提供的内部类的概念。

(顺便说一句,不要将字符串连接到username: password的命令行;这是一个严重的安全风险。请参阅subprocess模块以获得更安全,更简单的方法来传递参数。)

答案 1 :(得分:1)

我认为您不需要Domain类中的方法创建/删除帐户。我宁愿这样做:

class Account:
    def __init__(self, name, password, domain):
        ...

    def activate(self):
        self.domain.add(self)
        os.system('vadduser %s@%s %s' % (name, self.domain.name, password))

    def deactivate(self):
        self.domain.remove(self)
        os.system('vdeluser %s@%s' % (name, self.domain.name)

如果对象之间有很多这样的关系,我相信标准选项是使用数据库。 python最受欢迎的一个是SQLAlchemy。它将解决有效存储关系和查找它们(以及更多)的问题。但在你的例子中,它显然是一种矫枉过正,我想唯一的选择就是在我的代码中手动处理它。

答案 2 :(得分:1)

在Python中,人们倾向于避免做递归关系,因为垃圾收集器通常被实现为引用计数方案。

最简单的解决方案:执行容器中需要容器的操作。

更复杂的解决方案:当查询容器的对象时,创建一个临时代理对象,该对象包含对容器和包含对象的引用,并实现所包含的接口;并返回它而不是包含的对象。

答案 3 :(得分:0)

所以,你有域名,你有帐户,你的应用程序的真正工作是管理帐户,包括他们与域的关联......

为什么不创建一个Python“Manager”或“AccountManager”类,它可以是域和帐户的木偶主管?它会通过引入一个“客观第三方”来删除像您发布的那些问题,这些问题可以引用任何其他对象并随意建立它们之间的关联。

class Manager(object):
    def set_password(self, domain, account, password):
        os.system('vpasswd %s@%s %s' % (account.name, domain.name, password)
        account.password = password

>>> m = Manager()
>>> d = Domain('google.com')
>>> a = Account('foouser')
>>> m.set_password(d, a, p)

当然,在实际程序中,您需要创建一个管理器对象,然后根据需要使用该实例操作任意数量的域和帐户。