निर्भरता इंजेक्शन के लिए पायथन की विधि रिज़ॉल्यूशन ऑर्डर का उपयोग करना - क्या यह बुरा है?


11

मैंने रेमंड हेटिंगर की पाइकॉन वार्ता "सुपर कॉन्सिडर्ड सुपर" देखी और पायथन के एमआरओ (मेथड रेजोल्यूशन ऑर्डर) के बारे में थोड़ा सीखा, जो एक निर्धारक तरीके से एक वर्ग "माता-पिता" वर्गों को रैखिक करता है। निर्भरता इंजेक्शन करने के लिए, हम नीचे दिए गए कोड की तरह, अपने लाभ के लिए इसका उपयोग कर सकते हैं। तो अब, स्वाभाविक रूप से, मैं superहर चीज के लिए उपयोग करना चाहता हूं !

नीचे दिए गए उदाहरण में, Userवर्ग घोषित करता है कि यह दोनों से विरासत में मिली निर्भरता है LoggingServiceऔर UserService। यह विशेष रूप से विशेष नहीं है। दिलचस्प बात यह है कि हम यूनिट रिज़ॉल्यूशन के दौरान मेथड रिज़ॉल्यूशन ऑर्डर का उपयोग कर सकते हैं, निर्भरता का मज़ाक उड़ा सकते हैं। नीचे दिए गए कोड से एक MockUserServiceविरासत बनती है जो UserServiceहमारे द्वारा मॉक किए जाने के तरीकों को लागू करती है। नीचे दिए गए उदाहरण में, हम एक कार्यान्वयन प्रदान करते हैं validate_credentials। आदेश में MockUserServiceकिसी भी कॉल को संभालने के लिए validate_credentialsहमें इसे UserServiceMRO में पहले से रखने की आवश्यकता है । यह 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())

यह काफी चतुर लगता है, लेकिन क्या यह पायथन के कई विरासत और विधि संकल्प आदेश का एक अच्छा और वैध उपयोग है? जब मैं इस तरह से विरासत के बारे में सोचता हूं कि मैंने जावा के साथ OOP सीखा है तो यह पूरी तरह से गलत लगता है क्योंकि हम नहीं कह सकते हैं कि Userयह एक है UserServiceया Userएक है LoggingService। इस तरह से सोचना, विरासत का उपयोग करके जिस तरह से उपरोक्त कोड का उपयोग करता है, वह बहुत मायने नहीं रखता है। या यह है? यदि हम केवल कोड पुन: उपयोग प्रदान करने के लिए विशुद्ध रूप से विरासत का उपयोग करते हैं, और माता-पिता के बच्चों के रिश्तों के बारे में नहीं सोचते हैं, तो यह बहुत बुरा नहीं लगता है।

क्या मैं गलत कर रहा हूँ?


ऐसा लगता है कि यहां दो अलग-अलग प्रश्न हैं: "क्या इस तरह का एमआरओ हेरफेर सुरक्षित / स्थिर है?" और "क्या यह कहना गलत है कि पायथन वंशानुक्रम मॉडल" ए-ए "संबंध है?" क्या आप उन दोनों को, या उनमें से सिर्फ एक को पूछने की कोशिश कर रहे हैं? (वे दोनों अच्छे प्रश्न हैं, बस यह सुनिश्चित करना चाहते हैं कि हम सही उत्तर दें, या इसे दो प्रश्नों में विभाजित करें यदि आप दोनों नहीं चाहते हैं)
Ixrec

मैंने इसे पढ़ते हुए प्रश्नों को संबोधित किया है, क्या मैंने कुछ भी छोड़ दिया है?
आरोन हॉल

@lxrec मुझे लगता है कि आप बिल्कुल सही हैं। मैं दो अलग-अलग सवाल पूछने की कोशिश कर रहा हूं। मुझे लगता है कि इसका कारण "सही" नहीं लगता है क्योंकि मैं सोच रहा हूं कि "एक" वंशानुक्रम की शैली है (इसलिए GoldenRetriever "इस प्रकार के बजाय" डॉग एंड डॉग "है-एक" पशु "है) संरचनागत दृष्टिकोण। मुझे लगता है कि यह कुछ ऐसा है जिसके लिए मैं एक और सवाल खोल सकता हूं :)
Iain

यह भी मुझे काफी भ्रमित करता है। यदि रचना वंशानुक्रम के लिए बेहतर है, तो उपयोगकर्ता के निर्माण के लिए लॉगिंगस् सेवा और उपयोगकर्ता सेवा के उदाहरण क्यों न दें और उन्हें सदस्य के रूप में सेट करें? तब आप निर्भरता इंजेक्शन के लिए बतख टाइपिंग का उपयोग कर सकते हैं और इसके बजाय उपयोगकर्ता निर्माणकर्ता को MockUserService का एक उदाहरण दे सकते हैं। डीआई के लिए सुपर का उपयोग क्यों बेहतर है?
जेक स्पैचर 25'18

जवाबों:


7

निर्भरता इंजेक्शन के लिए पायथन की विधि रिज़ॉल्यूशन ऑर्डर का उपयोग करना - क्या यह बुरा है?

नहीं। यह C3 के रैखिककरण एल्गोरिथ्म का एक सैद्धांतिक उद्देश्यपूर्ण उपयोग है। यह आपके परिचित के खिलाफ है-एक संबंध है, लेकिन कुछ रचना को विरासत के लिए पसंद किया जाता है। इस मामले में, आपने कुछ संबंधों की रचना की है। ऐसा लगता है कि आप सही रास्ते पर हैं (हालाँकि पायथन में एक लॉगिंग मॉड्यूल है, इसलिए शब्दार्थ थोड़ा संदिग्ध है, लेकिन एक अकादमिक अभ्यास के रूप में यह पूरी तरह से ठीक है)।

मुझे नहीं लगता कि मज़ाक करना या बंदर-पीटना एक बुरी बात है, लेकिन अगर आप उन्हें इस पद्धति से बचा सकते हैं, तो आपके लिए अच्छा है - संयुक्त रूप से अधिक जटिलता के साथ, आपने उत्पादन वर्ग की परिभाषाओं को संशोधित करने से परहेज किया है।

क्या मैं गलत कर रहा हूँ?

अच्छा लग रहा है। आपने बंदर-पैच के बिना या मॉक पैच का उपयोग किए बिना एक संभावित महंगी विधि को ओवरराइड किया है, जिसका अर्थ है कि आपने उत्पादन वर्ग की परिभाषाओं को भी सीधे संशोधित नहीं किया है।

यदि इरादे वास्तव में परीक्षण में प्रमाणिकता के बिना कार्यक्षमता का उपयोग करना था, तो आपको शायद कुछ ऐसा करना चाहिए:

>>> print(MockUser('foo', 'bar').authenticate())
> MockUserService::validate_credentials
True

अपने वास्तविक क्रेडेंशियल्स का उपयोग करने के बजाय, और जांचें कि पैरामीटर सही तरीके से प्राप्त किए गए हैं, शायद मुखरता के साथ (जैसा कि यह परीक्षण है, आखिरकार)।

def validate_credentials(self, username, password):
    print('> MockUserService::validate_credentials')
    assert username_ok(username), 'username expected to be ok'
    assert password_ok(password), 'password expected to be ok'
    return True

अन्यथा, ऐसा लगता है कि आपने इसे समझ लिया है। आप एमआरओ को इस तरह सत्यापित कर सकते हैं:

>>> MockUser.mro()
[<class '__main__.MockUser'>, 
 <class '__main__.User'>, 
 <class '__main__.LoggingService'>, 
 <class '__main__.MockUserService'>, 
 <class '__main__.UserService'>, 
 <class 'object'>]

और आप सत्यापित कर सकते हैं कि इस MockUserServiceपर पूर्वता है UserService

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.