ฉันดู Pycon ของ Raymond Hettinger พูดว่า "Super Considered Super" และเรียนรู้เล็กน้อยเกี่ยวกับ PROON ของ MRO (Method Resolution Order) ซึ่งทำให้ชั้นเรียนเป็น "ผู้ปกครอง" ในลักษณะเชิงเส้น เราสามารถใช้สิ่งนี้เพื่อประโยชน์ของเราเช่นในรหัสด้านล่างเพื่อทำการฉีดพึ่งพา ตอนนี้ตามธรรมชาติฉันต้องการใช้super
สำหรับทุกสิ่ง!
ในตัวอย่างด้านล่างที่User
ระดับประกาศอ้างอิงมันโดยการสืบทอดจากทั้งสองและLoggingService
UserService
นี่ไม่ใช่สิ่งที่พิเศษเป็นพิเศษ ส่วนที่น่าสนใจคือเราสามารถใช้การแก้ไขวิธีสั่งซื้อยังจำลองการอ้างอิงในระหว่างการทดสอบหน่วย โค้ดด้านล่างนี้สร้างสิ่งMockUserService
ที่สืบทอดมาUserService
และนำเสนอวิธีการที่เราต้องการจำลอง validate_credentials
ในตัวอย่างด้านล่างเราให้การดำเนินการของ เพื่อที่จะได้MockUserService
จัดการกับการโทรใด ๆ ที่validate_credentials
เราจำเป็นต้องวางไว้ก่อนUserService
ใน MRO นี้ทำได้โดยการสร้างชั้นห่อหุ้มรอบUser
เรียกว่าMockUser
และมีมันสืบทอดมาจากและUser
MockUserService
ตอนนี้เมื่อเราทำMockUser.authenticate
และในทางกลับกันการเรียกใช้มาsuper().validate_credentials()
MockUserService
ก่อนUserService
ในการสั่งซื้อวิธีการแก้ปัญหาและเนื่องจากมีการใช้งานที่เป็นรูปธรรมของvalidate_credentials
การดำเนินการนี้จะถูกนำมาใช้ Yay - เราประสบความสำเร็จ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 หรือไม่? เมื่อฉันคิดเกี่ยวกับการรับมรดกในทางที่ผมได้เรียนรู้ OOP กับ Java รู้สึกนี้ผิดอย่างสมบูรณ์เพราะเราไม่สามารถพูดได้User
เป็นUserService
หรือเป็นUser
LoggingService
เมื่อคิดอย่างนั้นการใช้การสืบทอดวิธีที่โค้ดด้านบนใช้นั้นไม่สมเหตุสมผลเลย หรือมันคืออะไร? ถ้าเราใช้การถ่ายทอดทางพันธุกรรมเพียงอย่างเดียวเพื่อให้นำรหัสมาใช้ใหม่และไม่คิดในแง่ของความสัมพันธ์ระหว่างผู้ปกครอง -> เด็กสิ่งนี้ก็ไม่ได้เลวร้ายนัก
ฉันทำผิดหรือเปล่า?