我有一组相关的类,它们全部继承自一个基类。我想使用工厂方法来实例化这些类的对象。我想这样做是因为这样我可以在将对象返回给调用者之前将对象存储在以类名为键的字典中。然后,如果有对特定类的对象的请求,我可以检查我的字典中是否已存在该对象。如果没有,我将实例化它并将其添加到字典中。如果是这样,那么我将从字典中返回现有对象。这实际上会将我模块中的所有类变成单例。
我想这样做是因为它们继承的基类会对子类中的函数进行一些自动包装,并且我不希望这些函数被多次包装,这就是当前如果两个对象创建同一个类。
我能想到的唯一方法是检查堆栈跟踪__init__()
基类的方法,该方法将始终被调用,并且如果堆栈跟踪未显示创建对象的请求来自工厂函数,则抛出异常。
这是一个好主意吗?
Edit:这是我的基类的源代码。有人告诉我,我需要找出元类来更优雅地完成此任务,但这就是我现在所拥有的。所有 Page 对象都使用相同的 Selenium Webdriver 实例,该实例位于顶部导入的驱动程序模块中。该驱动程序的初始化成本非常高——它是在第一次创建 LoginPage 时初始化的。初始化后initialize()
方法将返回现有的驱动程序而不是创建新的驱动程序。这个想法是用户必须首先创建一个登录页面。最终将定义数十个 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()
如果一个页面被实例化了几次,那也没什么大不了的——方法只会被包装几次,并且会进行一些不必要的检查,但一切仍然有效。但如果一个页面被实例化数十、数百或数万次,那就很糟糕了。我可以在每个页面的类定义中放置一个标志,并检查方法是否已经被包装,但我喜欢保持类定义纯粹和干净的想法,并将所有的骗局推到页面的深处。我的系统没有人可以看到它并且它可以正常工作。