すべてが 1 つの基本クラスから継承される一連の関連クラスがあります。ファクトリ メソッドを使用して、これらのクラスのオブジェクトをインスタンス化したいと考えています。オブジェクトを呼び出し元に返す前に、クラス名をキーとする辞書にオブジェクトを格納できるため、これを行いたいと考えています。次に、特定のクラスのオブジェクトに対する要求がある場合、そのオブジェクトが辞書に既に存在するかどうかを確認できます。そうでない場合は、インスタンス化して辞書に追加します。もしそうなら、辞書から既存のオブジェクトを返します。これにより、基本的にモジュール内のすべてのクラスがシングルトンに変わります。
私がこれをしたいのは、それらすべてが継承する基本クラスがサブクラスの関数の自動ラッピングを行うためであり、関数が複数回ラップされることを望まないためです。同じクラスが作成されます。
これを行う唯一の__init__()
方法は、常に呼び出される基本クラスのメソッドでスタック トレースをチェックし、スタック トレースがオブジェクトを作成する要求が工場機能。
これは良い考えですか?
編集:これが私の基本クラスのソースコードです。これをよりエレガントに実現するには、メタクラスを理解する必要があると言われましたが、今のところはこれだけです。すべての Page オブジェクトは同じ Selenium Webdriver インスタンスを使用します。これは、上部にインポートされたドライバー モジュールにあります。このドライバは、初期化に非常にコストがかかります。LoginPage が初めて作成されるときに初期化されます。初期化後、initialize()
メソッドは新しいドライバーを作成する代わりに、既存のドライバーを返します。これは、ユーザーが LoginPage を作成することから始めなければならないという考え方です。最終的には何十もの Page クラスが定義され、それらは単体テスト コードによって使用され、Web サイトの動作が正しいことを検証します。
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()
ページが数回インスタンス化されても大したことではありません。メソッドは数回ラップされ、不要なチェックがいくつか行われますが、すべてが機能します。しかし、ページが何十回、何百回、何万回もインスタンス化されるのは良くありません。各ページのクラス定義にフラグを立てて、メソッドが既にラップされているかどうかを確認することもできますが、クラス定義を純粋かつクリーンに保ち、すべてのまやかしをページの奥深くに押し込むというアイデアが気に入っています。誰もそれを見ることができず、ただ機能する私のシステム。