उचित तरीके से बहुरूपता के साथ सशर्त बदलें?


10

दो वर्गों Dogऔर प्रोटोकॉल के Catअनुरूप दोनों पर विचार करें Animal(स्विफ्ट प्रोग्रामिंग भाषा के संदर्भ में। यह जावा / सी # में इंटरफ़ेस होगा)।

हमारे पास कुत्तों और बिल्लियों की मिश्रित सूची प्रदर्शित करने वाली एक स्क्रीन है। वहाँ Interactorवर्ग है जो पर्दे के पीछे तर्क को संभालता है।

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

func tryToDeleteModel(model: Animal) {
    if let model = model as? Cat {
        tellSceneToShowConfirmationAlert()
    } else if let model = model as? Dog {
        deleteModel(model: model)
    }
}

इस कोड को कैसे रिफैक्ट किया जा सकता है? यह स्पष्ट रूप से बदबू आ रही है

जवाबों:


9

आप प्रोटोकॉल प्रकार को स्वयं व्यवहार निर्धारित करने दे रहे हैं। आप अपने कार्यक्रम के दौरान सभी प्रोटोकॉल को लागू करना चाहते हैं , केवल कार्यान्वयन वर्ग को छोड़कर । इसे इस तरह से करने से लिस्कोव के प्रतिस्थापन सिद्धांत का सम्मान होता है, जो कहता है कि आपको Catया तो पारित करने में सक्षम होना चाहिए Dog(या किसी भी अन्य प्रोटोकॉल जो अंततः आपके पास हो सकते हैं Animal), और यह उदासीनता से काम करता है।

तो शायद आप एक जोड़ा जाने isCriticalके लिए समारोह Animalदोनों द्वारा लागू किया जाना Dogऔर Cat। लागू करने वाली कोई भी चीज़ Dogझूठी होगी और लागू करने वाली Catकोई भी चीज़ सही होगी।

उस बिंदु पर, आपको केवल करने की आवश्यकता होगी (यदि मेरा सिंटैक्स सही नहीं है, तो स्विफ्ट का उपयोगकर्ता नहीं):

func tryToDeleteModel(model: Animal) {
    if model.isCritical() {
        tellSceneToShowConfirmationAlert()
    } else {
        deleteModel(model: model)
    }
}

उस के साथ केवल एक छोटी सी समस्या है, और वह यह है कि Dogऔर Catप्रोटोकॉल हैं, जिसका अर्थ है कि वे अपने आप में यह निर्धारित नहीं करते हैं कि क्या isCriticalइसे लागू करने के लिए प्रत्येक कार्यान्वयन वर्ग को छोड़कर खुद के लिए निर्णय लेना है। यदि आपके पास बहुत सारे कार्यान्वयन हैं, तो संभवत: यह आपके लायक होगा कि आप एक विस्तार योग्य वर्ग का निर्माण Catकर सकते हैं या Dogजो पहले से ही सही तरीके से लागू होते हैं isCriticalऔर प्रभावी ढंग से ओवरराइड करने की आवश्यकता से सभी कार्यान्वयन कक्षाओं को साफ कर रहे हैं isCritical

यदि यह आपके प्रश्न का उत्तर नहीं देता है, तो कृपया टिप्पणियों में लिखें और मैं अपने उत्तर का विस्तार करूंगा!


यह थोड़ा प्रश्न के बयान में स्पष्ट नहीं है, लेकिन Dogऔर Catवर्ग के रूप में वर्णित किया गया है, जबकि Animalएक प्रोटोकॉल है कि उन वर्गों में से प्रत्येक के द्वारा लागू किया गया है। तो सवाल और आपके जवाब के बीच एक बेमेल संबंध है।
कालेब

तो आप मॉडल को यह तय करने का सुझाव देते हैं कि एक पुष्टिकरण पॉपअप प्रस्तुत करना है या नहीं? लेकिन क्या होगा अगर इसमें कोई भारी तर्क शामिल है, जैसे कि पॉप अप केवल 10 बिल्लियों को प्रदर्शित करने पर होता है? तर्क Interactorअब राज्य पर निर्भर करता है
एंड्री गोर्डीव

हाँ, अस्पष्ट प्रश्न के बारे में क्षमा करें, मैंने कुछ संपादन किए हैं। अब अधिक स्पष्ट होना चाहिए
एंड्री गोर्डीव

1
इस तरह के व्यवहार को मॉडल से नहीं जोड़ा जाना चाहिए। यह संदर्भ पर निर्भर करता है न कि इकाई से। मुझे लगता है कि बिल्ली और कुत्ता पीओजेओ होने की अधिक संभावना है। व्यवहार को अन्य स्थानों पर संभालना चाहिए और संदर्भ के अनुसार बदलने में सक्षम होना चाहिए। व्यवहार या तरीके जो व्यवहार कैट या डॉग पर भरोसा करेंगे, ऐसी कक्षाओं में बहुत अधिक जिम्मेदारियों को जन्म देंगे।
ग्रेजरी इलाहिमर

@ GrégoryElhaimer कृपया ध्यान दें कि यह व्यवहार का निर्धारण नहीं कर रहा है। यह केवल एक महत्वपूर्ण वर्ग है या नहीं, यह बताते हुए। कार्यक्रम के दौरान व्यवहार जो यह जानना जरूरी है कि यह एक महत्वपूर्ण वर्ग है तो उसके अनुसार मूल्यांकन और कार्य कर सकते हैं। यदि यह वास्तव में एक संपत्ति है जो दोनों में उदाहरणों को कैसे अलग करती है Catऔर Dogइसे संभाला जाता है, तो यह एक सामान्य संपत्ति हो सकती है Animal। कुछ और करने से बाद में रखरखाव सिरदर्द के लिए पूछ रहा है।
नील

4

बनाम पूछो

आप जिस सशर्त दृष्टिकोण को दिखा रहे हैं, उसे हम " पूछ " कहेंगे । यह वह जगह है जहां उपभोग करने वाला ग्राहक पूछता है "आप किस तरह के हैं?" और उनके अनुसार वस्तुओं के साथ अपने व्यवहार और बातचीत को अनुकूलित करता है।

इस विकल्प के साथ हम " बता " कहते हैं। बताओ का उपयोग करते हुए , आप पॉलीमॉर्फिक कार्यान्वयन में अधिक काम को धक्का देते हैं, ताकि उपभोक्ता क्लाइंट कोड सशर्त, सरल और बिना संभावित कार्यान्वयन के सरल हो।

चूंकि आप एक पुष्टिकरण चेतावनी का उपयोग करना चाहते हैं, इसलिए आप इंटरफ़ेस की एक स्पष्ट क्षमता बना सकते हैं। तो, आपके पास एक बूलियन विधि हो सकती है जो उपयोगकर्ता के साथ वैकल्पिक रूप से जांच करती है और पुष्टिकरण बूलियन को वापस करती है। जिन कक्षाओं में पुष्टि नहीं करनी है, वे बस के साथ ओवरराइड करते हैं return true;। अन्य कार्यान्वयन गतिशील रूप से यह निर्धारित कर सकते हैं कि क्या वे पुष्टि का उपयोग करना चाहते हैं।

उपभोग करने वाला ग्राहक हमेशा पुष्टिकरण विधि का उपयोग करेगा चाहे वह किसी विशेष उपवर्ग के साथ काम कर रहा हो, जो पूछने के बजाय बातचीत को बताता है

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


तो आप मॉडल को यह तय करने का सुझाव देते हैं कि एक पुष्टिकरण पॉपअप प्रस्तुत करना है या नहीं? लेकिन क्या होगा अगर इसमें कोई भारी तर्क शामिल है, जैसे कि पॉप अप केवल 10 बिल्लियों को प्रदर्शित करने पर होता है? तर्क Interactorअब राज्य पर निर्भर करता है
एंड्री गोर्डीव

2
ठीक है, हाँ, यह एक अलग सवाल है, एक अलग जवाब की आवश्यकता है।
एरिक इद्दत

2

यह निर्धारित करना कि क्या पुष्टि की आवश्यकता है Cat, कक्षा की जिम्मेदारी है , इसलिए इसे उस कार्य को करने में सक्षम करें। मैं कोटलिन को नहीं जानता, इसलिए मैं C # में चीजों को व्यक्त करूंगा। उम्मीद है कि विचारों को फिर कोटलिन के लिए भी स्थानांतरित किया जा सकता है।

interface Animal
{
    bool IsOkToDelete();
}

class Cat : Animal
{
    private readonly Func<bool> _confirmation;

    public Cat (Func<bool> confirmation) => _confirmation = confirmation;

    public bool IsOkToDelete() => _confirmation();
}

class Dog : Animal
{
    public bool IsOkToDelete() => true;
}

फिर, एक Catउदाहरण बनाते समय , आप इसे आपूर्ति करते हैं TellSceneToShowConfirmationAlert, जिसे trueहटाने के लिए ठीक होने पर वापस लौटना होगा :

var model = new Cat(TellSceneToShowConfirmationAlert);

और फिर आपका कार्य बन जाता है:

void TryToDeleteModel(Animal model) 
{
    if (model.IsOKToDelete())
    {
        DeleteModel(model)
    }
}

1
क्या यह मॉडल में हटाए गए तर्क को स्थानांतरित नहीं करता है? क्या इसे संभालने के लिए किसी अन्य वस्तु का उपयोग करना बेहतर नहीं होगा? संभवतः एक ApplicationService के अंदर एक शब्दकोश <कैट> की तरह एक डेटा संरचना; यह देखने के लिए जांचें कि क्या बिल्ली मौजूद है और यदि यह पुष्टि चेतावनी को बंद करने के लिए करता है?
keelerjr12

@ keelerjr12, यह निर्धारित करने की ज़िम्मेदारी को आगे बढ़ाता है कि क्या Catकक्षा में हटाए जाने के लिए पुष्टि की आवश्यकता है । मेरा तर्क है कि यह वह जगह है जहाँ यह है। यह तय करने के लिए नहीं मिलता है कि यह पुष्टि कैसे प्राप्त की जाती है (यह इंजेक्शन है) और यह स्वयं को नष्ट नहीं करता है। तो नहीं, यह मॉडल में हटाए गए तर्क को स्थानांतरित नहीं करता है।
डेविड अरनो 12

2
मुझे लगता है कि इस दृष्टिकोण से टन और यूआई-संबंधित कोड के टन को कक्षा से ही जोड़ा जाएगा। यदि वर्ग को कई UI परतों में उपयोग करने का इरादा है, तो समस्या बढ़ती है। हालांकि अगर यह एक व्यावसायिक इकाई के बजाय एक ViewModel प्रकार वर्ग है, तो यह उचित लगता है।
ग्राहम

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

1
बिल्कुल वही जो मुझे मिल रहा था। एक व्यावसायिक इकाई एक ViewModel वर्ग बनाम। बिजनेस डोमेन में, कैट को यूआई से संबंधित कोड के बारे में पता नहीं होना चाहिए। मेरा परिवार बिल्ली किसी को भी सचेत नहीं करता है। धन्यवाद!
keelerjr12

1

मैं एक विज़िटर पैटर्न के लिए जाने की सलाह दूंगा। मैंने जावा में एक छोटा सा कार्यान्वयन किया। मैं स्विफ्ट से परिचित नहीं हूं, लेकिन आप इसे आसानी से अपना सकते हैं।

आगंतुक

public interface AnimalVisitor<R>{
    R visitCat();
    R visitDog();
}

आपका मॉडल

abstract class Animal { // can also be an interface like VisitableAnimal
    abstract <R> R accept(AnimalVisitor<R> visitor);
}

class Cat extends Animal {
    public <R> R accept(AnimalVisitor<R> visitor) {
         return visitor.visitCat();
     }
}

class Dog extends Animal {
    public <R> R accept(AnimalVisitor<R> visitor) {
         return visitor.visitDog();
     }
}

आगंतुक को बुला रहा है

public void tryToDelete(Animal animal) {
    animal.accept( new AnimalVisitor<Void>() {
        public Void visitCat() {
            tellSceneToShowConfirmation();
            return null;
        }

        public Void visitDog() {
            deleteModel(animal);
            return null;
        }
    });
}

आप जितने चाहें पशुपालक के कार्यान्वयन कर सकते हैं।

उदाहरण:

public void isColorValid(Color color) {
    animal.accept( new AnimalVisitor<Boolean>() {
        public Boolean visitCat() {
            return Color.BLUE.equals(color);
        }

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