Raymond Hettinger'in Pycon'unun "Süper Süper Düşünülmüş" konuşmasını izledim ve bir sınıf "ebeveyn" sınıfını belirleyici bir şekilde lineerleştiren Python'un MRO'sunu (Yöntem Çözünürlük Sırası) biraz öğrendim. Bağımlılık enjeksiyonu yapmak için, aşağıdaki kodda olduğu gibi bunu kendi yararımıza kullanabiliriz. Şimdi, doğal olarak, super
her şey için kullanmak istiyorum !
Aşağıdaki örnekte User
sınıf, bağımlılıklarını her ikisinden LoggingService
ve UserService
. Bu çok özel değil. İlginç olan, Yöntem Çözüm Siparişi'ni birim testi sırasında bağımlılıkları taklit edebilmemizdir. Aşağıdaki kod , alay etmek istediğimiz yöntemlerden MockUserService
miras kalan UserService
ve bunların uygulanmasını sağlayan bir kod oluşturur . Aşağıdaki örnekte, bir uygulamasını sağlıyoruz validate_credentials
. MockUserService
Herhangi bir çağrıyı ele alabilmek için MRO'da validate_credentials
daha önce konumlandırmamız gerekir UserService
. Bu etrafında sarıcı sınıf oluşturarak yapılır User
adlandırılan MockUser
ve onu kalıt sahip User
ve MockUserService
.
Şimdi, bunu yaptığımızda MockUser.authenticate
ve bunun karşılığında, çağrılar Yöntem Çözüm Siparişinde super().validate_credentials()
MockUserService
daha önce UserService
ve validate_credentials
bu uygulamanın somut bir uygulamasını sunduğu için çağrılar kullanılacak. Yay - UserService
Birim testlerimizde başarılı bir şekilde alay ettik . Bunun UserService
pahalı ağ veya veritabanı çağrıları yapabileceğini düşünün - bunun gecikme faktörünü henüz kaldırdık. UserService
Canlı / ürün verilerine dokunma riski de yoktur .
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())
Bu oldukça zeki hissettiriyor, ancak bu Python'un çoklu kalıtım ve Yöntem Çözüm Emri'nin iyi ve geçerli bir kullanımı mı? Ben Java ile OOP öğrenilen bu şekilde miras düşündüğümüzde biz diyemeyiz çünkü tamamen yanlış bu hissettiğini User
bir olduğunu UserService
veya User
bir olduğunu LoggingService
. Bu şekilde düşünmek, mirasın yukarıdaki kodun kullandığı şekilde kullanılması pek mantıklı değildir. Yoksa öyle mi? Devralmayı yalnızca kodun yeniden kullanılmasını sağlamak ve ebeveyn- çocuk ilişkileri açısından düşünmemek için kullanırsak, bu o kadar da kötü görünmez.
Yanlış mı yapıyorum?