私はレイモンドヘッティンガーのPyconの講演「スーパー考慮スーパー」を見て、クラスの「親」クラスを決定論的な方法で線形化するPythonのMRO(メソッド解決順序)について少し学びました。これを使用して、以下のコードのように、依存性注入を行うことができます。だから今は当然、super
何にでも使いたい!
次の例では、User
クラスは、LoggingService
との両方から継承することで、依存関係を宣言していUserService
ます。これは特に特別なことではありません。興味深いのは、メソッド解決順序を使用して、単体テスト中に依存関係を模擬できることです。以下のコードは、モックしたいメソッドのMockUserService
継承UserService
と実装を提供するを作成します。以下の例では、の実装を提供していますvalidate_credentials
。へのMockUserService
呼び出しを処理validate_credentials
するにUserService
は、MROの前に配置する必要があります。これは、User
呼び出されるラッパークラスを作成し、MockUser
それをUser
and から継承させることで行われMockUserService
ます。
これを実行するMockUser.authenticate
と、次に、への呼び出しがメソッド解決順序のsuper().validate_credentials()
MockUserService
前UserService
にあり、validate_credentials
この実装の具体的な実装が提供されるため、これが使用されます。いいですね- UserService
単体テストでうまく模倣しました。UserService
コストのかかるネットワークやデータベースの呼び出しが発生する可能性があることを考慮してください。これにより、レイテンシ係数が削除されました。また、UserService
ライブ/製品データに触れるリスクもありません。
class LoggingService(object):
"""
Just a contrived logging class for demonstration purposes
"""
def log_error(self, error):
pass
class UserService(object):
"""
Provide a method to authenticate the user by performing some expensive DB or network operation.
"""
def validate_credentials(self, username, password):
print('> UserService::validate_credentials')
return username == 'iainjames88' and password == 'secret'
class User(LoggingService, UserService):
"""
A User model class for demonstration purposes. In production, this code authenticates user credentials by calling
super().validate_credentials and having the MRO resolve which class should handle this call.
"""
def __init__(self, username, password):
self.username = username
self.password = password
def authenticate(self):
if super().validate_credentials(self.username, self.password):
return True
super().log_error('Incorrect username/password combination')
return False
class MockUserService(UserService):
"""
Provide an implementation for validate_credentials() method. Now, calls from super() stop here when part of MRO.
"""
def validate_credentials(self, username, password):
print('> MockUserService::validate_credentials')
return True
class MockUser(User, MockUserService):
"""
A wrapper class around User to change it's MRO so that MockUserService is injected before UserService.
"""
pass
if __name__ == '__main__':
# Normal useage of the User class which uses UserService to resolve super().validate_credentials() calls.
user = User('iainjames88', 'secret')
print(user.authenticate())
# Use the wrapper class MockUser which positions the MockUserService before UserService in the MRO. Since the class
# MockUserService provides an implementation for validate_credentials() calls to super().validate_credentials() from
# MockUser class will be resolved by MockUserService and not passed to the next in line.
mock_user = MockUser('iainjames88', 'secret')
print(mock_user.authenticate())
これはかなり賢く感じられますが、これはPythonの多重継承とメソッド解決順序の有効かつ有効な使用ですか?私は、私は、JavaとOOPを学んだ方法で、継承について考えるとき、我々が言うことができないので、完全に間違ってこの感触はUser
あるUserService
かUser
ですLoggingService
。そのように考えると、上記のコードが使用する方法で継承を使用することは、あまり意味がありません。またはそれは?コードの再利用を提供するためだけに継承を使用し、親と子の関係を考慮しない場合、これはそれほど悪くないように見えます。
私はそれを間違っていますか?