我有一组相关的类,它们都继承自一个基类。我想使用工厂方法来实例化这些类的对象。我想这样做是因为我可以在将对象返回给调用者之前将对象存储在由类名键入的字典中。然后,如果有对特定类的对象的请求,我可以检查一个对象是否已经存在于我的字典中。如果没有,我将实例化它并将其添加到字典中。如果是这样,那么我将从字典中返回现有对象。这实际上会将我模块中的所有类变成单例。
我想这样做是因为它们都继承自的基类对子类中的函数进行了一些自动包装,而且我不希望函数被多次包装,如果两个对象当前会发生这种情况创建相同的类。
我能想到的唯一方法是检查__init__()
基类方法中的堆栈跟踪,该方法将始终被调用,如果堆栈跟踪未显示创建对象的请求来自工厂功能。
这是一个好主意吗?
编辑:这是我的基类的源代码。有人告诉我,我需要找出元类来更优雅地完成这项工作,但这就是我现在所拥有的。所有 Page 对象都使用相同的 Selenium Webdriver 实例,该实例位于顶部导入的驱动程序模块中。这个驱动程序的初始化成本很高——它在第一次创建 LoginPage 时被初始化。初始化后,该initialize()
方法将返回现有驱动程序而不是创建新驱动程序。这个想法是用户必须从创建一个 LoginPage 开始。最终将定义数十个 Page 类,它们将被单元测试代码用于验证网站的行为是否正确。
from driver import get_driver, urlpath, initialize
from settings import urlpaths
class DriverPageMismatchException(Exception):
pass
class URLVerifyingPage(object):
# we add logic in __init__() to check the expected urlpath for the page
# against the urlpath that the driver is showing - we only want the page's
# methods to be invokable if the driver is actualy at the appropriate page.
# If the driver shows a different urlpath than the page is supposed to
# have, the method should throw a DriverPageMismatchException
def __init__(self):
self.driver = get_driver()
self._adjust_methods(self.__class__)
def _adjust_methods(self, cls):
for attr, val in cls.__dict__.iteritems():
if callable(val) and not attr.startswith("_"):
print "adjusting:"+str(attr)+" - "+str(val)
setattr(
cls,
attr,
self._add_wrapper_to_confirm_page_matches_driver(val)
)
for base in cls.__bases__:
if base.__name__ == 'URLVerifyingPage': break
self._adjust_methods(base)
def _add_wrapper_to_confirm_page_matches_driver(self, page_method):
def _wrapper(self, *args, **kwargs):
if urlpath() != urlpaths[self.__class__.__name__]:
raise DriverPageMismatchException(
"path is '"+urlpath()+
"' but '"+urlpaths[self.__class.__name__]+"' expected "+
"for "+self.__class.__name__
)
return page_method(self, *args, **kwargs)
return _wrapper
class LoginPage(URLVerifyingPage):
def __init__(self, username=username, password=password, baseurl="http://example.com/"):
self.username = username
self.password = password
self.driver = initialize(baseurl)
super(LoginPage, self).__init__()
def login(self):
driver.find_element_by_id("username").clear()
driver.find_element_by_id("username").send_keys(self.username)
driver.find_element_by_id("password").clear()
driver.find_element_by_id("password").send_keys(self.password)
driver.find_element_by_id("login_button").click()
return HomePage()
class HomePage(URLVerifyingPage):
def some_method(self):
...
return SomePage()
def many_more_methods(self):
...
return ManyMorePages()
如果一个页面被实例化了几次,这没什么大不了的——这些方法只会被包装几次,并且会发生一些不必要的检查,但一切仍然有效。但是如果一个页面被实例化几十、几百或几万次,那就不好了。我可以在每个页面的类定义中放置一个标志并检查方法是否已经被包装,但我喜欢保持类定义纯净和干净并将所有 hocus-pocus 推入深角落的想法我的系统没有人可以看到它,它只是工作。