নির্ভরতা ইনজেকশনের জন্য পাইথনের পদ্ধতি রেজোলিউশন অর্ডার ব্যবহার করা - এটি কি খারাপ?


11

আমি রেমন্ড হেট্টিনজারের পাইকন টক "সুপার বিবেচিত সুপার" দেখেছি এবং পাইথনের এমআরও (মেথড রেজোলিউশন অর্ডার) সম্পর্কে কিছুটা শিখেছি যা একটি ডিটারমিনিস্টিক উপায়ে "অভিভাবক" শ্রেণিগুলিকে লাইন করে তোলে। নির্ভরতা ইনজেকশন করতে আমরা নীচের কোডের মতো এটি আমাদের সুবিধার জন্য ব্যবহার করতে পারি। তাই এখন, স্বাভাবিকভাবেই, আমি superসমস্ত কিছুর জন্য ব্যবহার করতে চাই !

নীচের উদাহরণে, Userবর্গ উভয় থেকে inheriting দ্বারা এটি এর নির্ভরতা ঘোষণা LoggingServiceএবং UserService। এটি বিশেষভাবে বিশেষ নয়। মজার অংশটি হ'ল আমরা ইউনিট টেস্টিংয়ের সময় মেথড রেজোলিউশন অর্ডারটি ম্যাক আউট নির্ভরতাগুলিও ব্যবহার করতে পারি। নীচের কোডটি এমন একটি তৈরি করে MockUserServiceযা উত্তরাধিকার সূত্রে প্রাপ্ত UserServiceএবং আমরা যে পদ্ধতিগুলি উপহাস করতে চাই তার একটি বাস্তবায়ন সরবরাহ করে। নীচের উদাহরণে, আমরা এর একটি বাস্তবায়ন সরবরাহ করি validate_credentialsMockUserServiceকোনও কল হ্যান্ডেল করার জন্য আমাদের এমআরওতে validate_credentialsআগে এটি অবস্থান করা উচিত UserService। এই রাপার ক্লাস প্রায় তৈরি দ্বারা সম্পন্ন Userনামক MockUserএবং এ থেকেই উত্তরাধিকারী থাকার Userএবং 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())

এটি বেশ চতুর মনে হলেও এটি কি পাইথনের একাধিক উত্তরাধিকার এবং পদ্ধতি সমাধানের আদেশের একটি ভাল এবং বৈধ ব্যবহার? আমি যখন যে আমি জাভা সঙ্গে গলি শিখেছি ভাবে উত্তরাধিকার সম্পর্কে চিন্তা সম্পূর্ণ ভুল, কারণ আমরা বলতে পারি না এই মতানুযায়ী Userএকটি হল UserServiceবা Userএকটি হল LoggingService। সেভাবে চিন্তা করে, উপরের কোডটি যেভাবে উত্তরাধিকার ব্যবহার করে তা ব্যবহার করে তা বোঝা যায় না। অথবা এটা? আমরা যদি কোডটি পুনরায় ব্যবহারের জন্য, এবং পিতামাতার-> সন্তানের সম্পর্কের ক্ষেত্রে চিন্তা না করে খালি উত্তরাধিকার ব্যবহার করি তবে এটিকে এত খারাপ বলে মনে হয় না।

আমি কি এটা ভুল করছি?


দেখে মনে হচ্ছে এখানে দুটি পৃথক প্রশ্ন রয়েছে: "এমআরও ম্যানিপুলেশন এই ধরণের কি নিরাপদ / স্থিতিশীল?" এবং "পাইথনের উত্তরাধিকারের মডেলগুলি" সম্পর্ক "একটি" সম্পর্ক? আপনি কি এই দুজনকেই জিজ্ঞাসা করার চেষ্টা করছেন, বা কেবল তাদের একজনকে? (তারা উভয়ই ভাল প্রশ্ন, কেবল নিশ্চিত হয়েছি যে আমরা সঠিক উত্তরটি দিয়েছি, বা আপনি দুটিই না চাইলে এটিকে দুটি প্রশ্নে বিভক্ত করুন)
Ixrec

প্রশ্নগুলি পড়ার সাথে সাথে আমি সম্বোধন করেছি, আমি কি কিছু রেখেছি?
অ্যারন হল

@lxrec আমি মনে করি আপনি একদম ঠিক আছেন। আমি দুটি ভিন্ন প্রশ্ন জিজ্ঞাসা করার চেষ্টা করছি। আমি মনে করি যে এটি "সঠিক" বোধ করে না কারণ কারণ আমি এই ধরণের পরিবর্তে "হ'ল" উত্তরাধিকারের স্টাইল (তাই গোল্ডেনর্ট্রিভার "হ'ল" কুকুর এবং কুকুর "" একটি প্রাণী) রচনাগত পদ্ধতির। আমি মনে করি এটি এমন একটি বিষয় যা আমি এর জন্য আরেকটি প্রশ্ন খুলতে পারি :)
আয়ান

এটিও আমাকে উল্লেখযোগ্যভাবে বিভ্রান্ত করে। যদি রচনাটি উত্তরাধিকারের চেয়ে অগ্রাধিকারযোগ্য হয় তবে লগিং সার্ভিস এবং ব্যবহারকারীর পরিষেবাগুলির ব্যবহারকারীর কনস্ট্রাক্টরের কাছে না কেন এবং সেগুলি সদস্য হিসাবে সেট করুন? তারপরে আপনি নির্ভরতা ইনজেকশনের জন্য হাঁসের টাইপিং ব্যবহার করতে পারেন এবং এর পরিবর্তে মক ইউজারসেবার একটি উদাহরণ ব্যবহারকারীর কাছে পাস করতে পারেন। ডিআই এর জন্য সুপার ব্যবহার করা কেন বেশি পছন্দনীয়?
জ্যাক স্প্রেচার

উত্তর:


7

নির্ভরতা ইনজেকশনের জন্য পাইথনের পদ্ধতি রেজোলিউশন অর্ডার ব্যবহার করা - এটি কি খারাপ?

না এটি সি 3 লিনিয়ারাইজেশন অ্যালগরিদমের একটি তাত্ত্বিক উদ্দেশ্যে ব্যবহার। এটি আপনার পরিচিত সম্পর্কের বিরুদ্ধে যায় তবে কিছু রচনাটিকে উত্তরাধিকারের চেয়ে অগ্রাধিকার হিসাবে বিবেচনা করে। এক্ষেত্রে আপনার কিছু সম্পর্ক রয়েছে। দেখে মনে হচ্ছে আপনি সঠিক পথে রয়েছেন (যদিও পাইথনের লগিং মডিউল রয়েছে তাই শব্দার্থবিজ্ঞান কিছুটা প্রশ্নবিদ্ধ, তবে একাডেমিক অনুশীলন হিসাবে এটি পুরোপুরি ঠিক আছে)।

আমি ভাবি না যে মশকরা বা বানর-প্যাচিং একটি খারাপ জিনিস, তবে আপনি যদি এই পদ্ধতিটি এগুলি এড়াতে পারেন তবে আপনার পক্ষে ভাল - অবশ্যই আরও জটিলতার সাথে আপনি উত্পাদন শ্রেণীর সংজ্ঞা সংশোধন করা এড়িয়ে গেছেন।

আমি কি এটা ভুল করছি?

ভাল লাগছে। আপনি একটি সম্ভাব্য ব্যয়বহুল পদ্ধতি ওভাররাইড করেছেন, বানর-প্যাচিং বা মক প্যাচ ব্যবহার না করে, যার অর্থ, আপনি সরাসরি উত্পাদন শ্রেণীর সংজ্ঞাগুলিও সংশোধন করেননি।

যদি উদ্দেশ্যটি হ'ল পরীক্ষায় শংসাপত্র না রেখে কার্যকারিতাটি ব্যবহার করা হয় তবে আপনার সম্ভবত এমন কিছু করা উচিত:

>>> 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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.