Форум сайта python.su
Добрый день.
Есть дерево наследования, обрабатывающее HTTP запросы. И есть острое желание сделать работу “фабрики классов” (если этот термин вообще можно здесь применить) пошаговой, с определением конкретных типов по мере углубления в реальную структуру сайта, примерно так:
class Root(object):
def __init__(self):
self.__status = {200: 'Ok', 404: 'NotFound',}
self.__headers = []
def SetStatus(self, status_code):
self.__headers.append('Status: %d %s' % status_code, self.__status[status_code])
def Reply(self, url):
if self.__BadUrl(url):
self.SetStatus(404)
content = None
else:
if url.startswith('/admin'):
self.__class__ = AdminEngine
else:
self.__class__ = UserEngine
content = self.Reply(url)
r = '\n'.join(self.__headers) + '\n\n'
if content: r += content
return r
class AdminEngine(Root):
def Reply(self, url):
self.SetStatus(200)
if self.LogOnSuccess():
self.__InitDB('adminlogin', 'adminpass')
return self.InnerShrine()
else: return self.LogOn()
Отредактировано (Фев. 24, 2011 14:37:02)
Офлайн
За self.__class__ = AdminEngine вам когда-нибудь дадут по рукам.
Офлайн
Есть идея, как реализовать функционал корректнее, или тебе так, чисто постебаться?
Офлайн
Ладно, перефразируем задачу.
Есть класс Engine. Он знает, какие урлы потенциально кошерны, а какие - заведомо нет и должны окончиться 404 ответом. Он знает, как начинается путь к админке, и знает, что остальные пути - для пользователей сайта.
Есть класс UserEngine(Engine). Он знает, что урлы должны начинаться с «http://domain.ru/» и заканчиваться на «/», и при необходимости легко отдаст 403 редирект. И еще он знает, что на /gallery/ висит галерея, на /news/ - новости, а все остальные пути потенциально ведут к некоторым статьям. Также он устанавливает подключение к СУБД с ограниченными (или не очень) правами, в зависимости от контекста.
Есть класс AdminEngine(Engine). Ему пофигу, как начинаются и заканчиваются урлы, до тех пор, пока он может их обработать. И он знает, какой именно потомок должен отработать по каждому разделу админки. А еще он знает, что сперва надо посмотреть на запрос и при необходимости вызвать соответствующий обработчик соответствующего потомка, после чего почистить параметры и дать редирект для избежания проблем с F5.
Есть несколько классов: ArticleEngine(UserEngine), … - они работают каждый по своему разделу, но всем им нужна БД с одинаковыми правами. И есть еще несколько классов: ArticleEditor(AdminEngine), … - аналогично.
На мой взгляд, это иерархия наследования чистой воды. Но вот незадача: приступая к разбору адреса, я НЕ ЗНАЮ, какой конкретно обработчик должен быть создан. На каждом уровне наследования известно только одно: пора ли уже вернуть что-то определенное, или надо спихнуть работу на кого-то из младших. Это позволяет, в частности, легко подключать новые секции без ведома отцов. Вытаскивать все в отдельную фабрику - значит основательно положить на инкапсуляцию, со всеми вытекающими. Не говоря уже о том, КАК эта фабрика будет выглядеть.
Офлайн
self.__class__ = NewClass - никак не определенная операция в стандарте.
Т.е. стандарт ее не разрешает, но и не запрещает явно.
CPython позволяет это делать. С оговорками - некоторые существующие библиотеки очень не любят подобные действия.
Jython, насколько я помню, лоялен. Pypy - ломался.
Ну ладно. Кого волнуют эти экзотические диалекты да сторонние библиотеки. Пример-то работает?
Ну как работает… По крайней мере не успело сломаться. Хотя вы уже ощутили неудобство с взаимозависимостями. Это - плохой запах дурного дизайна.
А как вы собираетесь гарантировать, что новоподставленный класс полностью совместим со старым? Например, AdminEngine.Reply не потребует наличия атрибута, которого в Root просто не было?
Кто будет переключать обратно на Root? А если никто - то отчего бы сразу не создать AdminEngine и дело с концом.
В известных мне системах с path traversal новый resource создается на каждый элемент url path. Или используется текущий на данном шаге. И - никогда не меняется через грязный хак.
В общем, ваша схема может иногда работать, а по мере расширения станет ломаться в странных местах. Вы ее будете чинить, но до конца ликвидировать все проблемы невозможно.
Офлайн
Как по мне - не нужна Engine. А привык думать о таком в терминах traversal.
Посмотрите на картинку из http://docs.pylonsproject.org/projects/pyramid/1.0/narr/traversal.html
Офлайн
Пример - да, работает, хотя конкретно те строки, которые я привел в качестве примера, написаны от балды. Переключать тип объектов-обработчиков обратно на корень не требуется, ибо все, что от них нужно - конкретный HTTP ответ, потом смерть.
AdminEngine.Reply не потребует наличия атрибута, которого в Root не было, потому что открытых данных-членов в Root (как и в большинстве прочих классов) вообще нет. Все строго через функции доступа. Я так понимаю, вопрос по совместимости возник из моего предположения отказаться от наследования? Иначе смысл вообще не понятен.
Ладно, раз замена __class__ - хак, я согласен с тем, что это плохо и может внезапно отлиться хорошим геморроем. Я думал, раз интерпретатор кушает - значит, все в порядке )) Traversal сейчас почитаю, спасибо.
Офлайн
Да. У вас был Root.Reply, который внезапно стал AdminEngine.Reply. Программиста вроде бы никто не предупреждал, что в AdminEngine можно делать не всё, что было бы доступно при традиционной работе с классами. Например, конструктор никогда не вызывается и т.д. Это обескураживает.
То, что я вам предлагаю - разбить url path на сегменты через ‘/’, а потом подсовывая очередной сегмент ресурсу получать новый ресурс. Которому опять давать следующий сегмент. Почти как у вас, только создавать новый экземпляр в случае нужды вместо мутирования текущего.
Офлайн