Python: как создать контейнер с элементами, которые должны ссылаться на их контейнер - PullRequest
3 голосов
/ 15 апреля 2010

(По общему признанию, название не слишком велико. Пожалуйста, простите мой английский, это лучшее, что я мог придумать.)

Я пишу скрипт на python, который будет управлять почтовыми доменами и их учетными записями, и я также новичок в ООП-дизайне. Мои две (связанные?) Проблемы:

  1. Класс Domain должен выполнять специальную работу по добавлению и удалению учетных записей, например добавлять / удалять их в базовой реализации
  2. как управлять операциями с учетными записями, которые должны проходить через их контейнер

Чтобы решить предыдущую проблему, я бы добавил фабричный метод в класс Domain, который создаст экземпляр Account в этом домене, и метод remove ( anti-factory ?) Для обрабатывать удаления.

Для последнего это кажется мне «анти-опа», поскольку логически это будет операция с учетной записью (например, изменение пароля), которая всегда должна ссылаться на содержащий домен. Мне кажется, что я должен добавить ссылку на домен в учетную запись и использовать ее для получения данных (например, имени домена) или вызова методов в классе домена.

Пример кода (элемент использует данные из контейнера), который управляет базовой 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 ]

2 голосов
/ 15 апреля 2010

Для операций, которые вы обрисовали в общих чертах, не ясно, что вам вообще нужно Account. Единственная информация, которую он хранит, но которая еще не продублирована в Domain, - это пароль. Вместо этого вы можете просто указать Domain.accounts как username: password.

Не умножайте классы, несущие идентичность, пока вам это не нужно.

Для чего стоит в общем случае, да, когда у вас есть объекты, которые принадлежат другим объектам, вполне нормально дать им ссылку на их владельца и заставить их общаться вверх по мере необходимости. В Python нет понятия внутренних классов, которые некоторые языки предоставляют для владения.

(Между прочим, не объединяйте строки в командные строки для os.system; это серьезная угроза безопасности. См. Модуль подпроцесс для более безопасного и простого способа передачи параметров.)

1 голос
/ 15 апреля 2010

В Python стараются избегать создания рекурсивных отношений, потому что сборщик мусора обычно реализуется как схема подсчета ссылок.

Самое простое решение: выполните операции, для которых нужен контейнер в контейнере.

Немного более запутанное решение: когда контейнер запрашивается для объекта, создайте временный прокси-объект, который содержит ссылку как на контейнер, так и на содержащийся объект, и реализует интерфейс содержимого; и вернуть его вместо содержимого объекта.

1 голос
/ 15 апреля 2010

Я думаю, что вам не нужны методы создания / удаления учетной записи в классе 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 . Это решит проблему эффективного хранения отношений и их поиска (и многое другое). Но в вашем примере это явно перебор, и я полагаю, что единственный вариант - обработать это вручную, как в моем коде.

0 голосов
/ 16 апреля 2010

Итак, у вас есть домены, и у вас есть учетные записи, и реальная работа вашего приложения - управление учетными записями, включая их связь с доменами ...

Почему бы вам просто не создать класс 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)

Конечно, в реальной программе вы создаете один объект менеджера, а затем воздействуете на столько доменов и учетных записей, сколько хотите с этим одним экземпляром.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...