Python 如何设计一个容器,其元素必须引用其容器

60 阅读2分钟

用户正在编写一个 Python 脚本,用于管理电子邮件域及其帐户,并且还是 OOP 设计的新手。用户遇到两个相关的问题:

  1. Domain 类必须执行特殊的工作来添加和删除帐户,例如将其添加到/从底层实现中添加/删除。
  2. 如何管理必须通过其容器进行的操作。

huake_00152_.jpg

2、解决方案

对于第一个问题,用户可以向 Domain 类添加一个工厂方法,该方法将在该域中构建一个 Account 实例,并添加一个“删除”(反工厂?)方法来处理删除。

对于第二个问题,这对于用户来说似乎是“反 OOP”,因为逻辑上是 Account 上的操作(例如更改密码)必须始终引用包含的 Domain。对于用户而言,必须向 Account 中添加对 Domain 的引用,并使用该引用获取数据(例如域名称)或调用 Domain 类上的方法。

用户提供了一个代码示例,该示例管理一个底层的 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 方法来执行实际工作,但用户认为听起来同样糟糕。

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

3、总结

针对用户的这两个问题,有以下几条建议:

  1. 对于用户描述的操作,目前尚不清楚是否需要 Account。Account 中唯一包含的信息是 Domain 中未重复的信息是密码。用户可以只让 Domain.accounts 查找用户名:密码。不要在需要之前增加标识承载类。

  2. 通常,当用户拥有由其他对象拥有的对象时,给它们一个指向其所有者的引用,并根据需要向上进行通信是完全正常的。Python 没有一些语言为所有权提供的内部类概念。

  3. 对于用户问题中提到的示例,用户不需要在 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)