कैसे कोई तर्क को कम रखता है और फिर भी तीसरे पक्ष की निर्भरता को अलग रखता है?


13

मैं थर्ड पार्टी लाइब्रेरी का उपयोग करता हूं। वे मुझे एक POJO पास करते हैं, जो हमारे इरादों और उद्देश्यों के लिए, संभवतः इस तरह से लागू किया जाता है:

public class OurData {
  private String foo;
  private String bar;
  private String baz;
  private String quux;
  // A lot more than this

  // IMPORTANT: NOTE THAT THIS IS A PACKAGE PRIVATE CONSTRUCTOR
  OurData(/* I don't know what they do */) {
    // some stuff
  }

  public String getFoo() {
    return foo;
  }

  // etc.
}

सहित कई कारणों से, लेकिन उनके एपीआई एनकैप्सुलेट करने और यूनिट परीक्षण की सुविधा तक सीमित नहीं है, मैं उनके डेटा को लपेटना चाहता हूं। लेकिन मैं नहीं चाहता कि मेरे मुख्य वर्ग उनके डेटा (फिर, परीक्षण कारणों के लिए) पर निर्भर रहें! तो अभी मेरे पास कुछ ऐसा है:

public class DataTypeOne implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
  }
}

public class DataTypeTwo implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz, String quux) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
    this.quux = quux;
  }
}

और फिर यह:

public class ThirdPartyAdapter {
  public static makeMyData(OurData data) {
    if(data.getQuux() == null) {
      return new DataTypeOne(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
      );
    } else {
      return new DataTypeTwo(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
        data.getQuux();
      );
  }
}

यह एडेप्टर वर्ग अन्य कुछ वर्गों के साथ मिलकर बनता है जो MUST को थर्ड पार्टी एपीआई के बारे में जानते हैं, यह मेरे सिस्टम के बाकी हिस्सों के माध्यम से व्यापकता को सीमित करता है। हालाँकि ... यह समाधान सकल है! स्वच्छ संहिता में, पृष्ठ 40:

तीन से अधिक तर्कों (पॉलीएडिक) के लिए बहुत विशेष औचित्य की आवश्यकता होती है - और फिर वैसे भी इसका उपयोग नहीं किया जाना चाहिए।

जिन चीजों पर मैंने विचार किया है:

  • स्थैतिक सहायक विधि के बजाय एक फैक्ट्री ऑब्जेक्ट बनाना
    • एक bajillion तर्क होने की समस्या को हल नहीं करता है
  • DataTypeOne और DataTypeTwo का एक उपवर्ग बनाना, जिसमें आश्रित कंस्ट्रक्टर हो
    • अभी भी एक पॉलीएडिक संरक्षित कंस्ट्रक्टर है
  • समान इंटरफ़ेस के अनुरूप पूरी तरह से अलग कार्यान्वयन बनाएं
  • एक साथ उपरोक्त विचारों के कई

इस स्थिति को कैसे संभाला जाना चाहिए?


ध्यान दें कि यह भ्रष्टाचार-विरोधी परत की स्थिति नहीं है। उनके एपीआई में कुछ भी गलत नहीं है। समस्याएं हैं:

  • मैं नहीं चाहता कि मेरी डेटा संरचनाएं हों import com.third.party.library.SomeDataStructure;
  • मैं अपने परीक्षण मामलों में उनकी डेटा संरचनाओं का निर्माण नहीं कर सकता
  • मेरे वर्तमान समाधान में बहुत ही उच्च तर्क मायने रखता है। मैं उनके डेटा स्ट्रक्चर्स में जाने के बिना तर्क को कम रखना चाहता हूं।
  • वह सवाल है " भ्रष्टाचार विरोधी परत क्या है?"। मेरा सवाल है " मैं इस परिदृश्य को हल करने के लिए किसी पैटर्न, किसी भी पैटर्न का उपयोग कैसे कर सकता हूं?"

मैं कोड के लिए नहीं कह रहा हूं, या तो (अन्यथा यह सवाल एसओ पर होगा), बस मुझे जवाब को प्रभावी ढंग से लिखने के लिए सक्षम करने के लिए पर्याप्त उत्तर के लिए पूछ रहा था (जो कि प्रश्न प्रदान नहीं करता है)।


यदि कई ऐसे 3 पार्टी POJO हैं, तो यह कस्टम टेस्ट कोड लिखने के प्रयास के लायक हो सकता है जो आपके टेस्ट इनपुट के रूप में कुछ सम्मेलनों (जैसे कि नाम कुंजी int_bar) के साथ मैप का उपयोग करता है। या कुछ कस्टम मध्यस्थ कोड के साथ JSON या XML का उपयोग करें। वास्तव में, com.thirdparty के परीक्षण के लिए एक DSL की तरह।
user949300

स्वच्छ संहिता से पूर्ण उद्धरण:The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification — and then shouldn’t be used anyway.
लीलिएन्थल

11
एक पैटर्न या प्रोग्रामिंग दिशानिर्देश का अंधा पालन स्वयं का प्रतिमान है
लीलिएन्थल

2
"उनके एपीआई को एनकैप्सुलेट करना और यूनिट टेस्टिंग की सुविधा देना" लगता है कि यह मेरे लिए ओवर-टेस्टिंग और / या टेस्ट-प्रेरित डिज़ाइन डैमेज (या संकेत है कि आप इसे शुरू करने के लिए अलग तरीके से डिजाइन कर सकते हैं) का मामला हो सकता है। अपने आप को इस पूछें: इस करता है वास्तव में अपने कोड समझने में आसान और परिवर्तन और पुन: उपयोग करने के लिए बनाते हैं? मैं अपना पैसा "नहीं" पर रखूंगा। इस बात की कितनी वास्तविक संभावना है कि आप कभी भी इस पुस्तकालय की अदला-बदली करेंगे? शायद बहुत नहीं। यदि आप इसे स्वैप करते हैं, तो क्या यह वास्तव में जगह में एक पूरी तरह से अलग ड्रॉप करने के लिए कोई आसान बनाता है? फिर, मैं "नहीं" पर शर्त लगाऊंगा।
jpmc26

1
@JamesAnderson मैंने सिर्फ पूरा उद्धरण पुन: पेश किया क्योंकि मुझे यह दिलचस्प लगा लेकिन यह मेरे लिए स्निपेट से स्पष्ट नहीं था कि क्या यह सामान्य रूप से कार्य करता है या विशेष रूप से निर्माण करने वालों के लिए। मैं दावे का समर्थन करने का मतलब नहीं था और जैसा कि jpmc26 ने कहा, मेरी अगली टिप्पणी आपको कुछ संकेत देनी चाहिए जो मैं ऐसा नहीं कर रहा था। मुझे यकीन नहीं है कि आपको शिक्षाविदों पर हमला करने की आवश्यकता क्यों महसूस होती है, लेकिन पॉलीसिलेबल्स का उपयोग करके किसी को एक शैक्षिक अभिजात्य वर्ग के ऊपर अपने हाथी दांत टॉवर पर खड़ा नहीं किया जाता है।
लिलिएनथाल

जवाबों:


10

जब मैंने कई इनिशियलाइज़ेशन पैरामीटर्स का इस्तेमाल किया है, तो एक टाइप तैयार करना है, जिसमें सिर्फ़ इनिशियलाइज़ेशन के पैरामीटर हैं

public class DataTypeTwoParameters {
    public String foo;  // use getters/setters instead if it's appropriate
    public int bar;
    public double baz;
    public String quuz;
}

उसके बाद DataTypeTwo के लिए कंस्ट्रक्टर DataTypeTwoParameters ऑब्जेक्ट लेता है, और DataTypeTwo का निर्माण इस प्रकार होता है:

DataTypeTwoParameters p = new DataTypeTwoParameters();
p.foo = "Hello";
p.bar = 4;
p.baz = 3;
p.quuz = "World";

DataTypeTwo dtt = new DataTypeTwo(p);

इससे यह स्पष्ट करने का बहुत मौका मिलता है कि DataTypeTwo में जाने वाले सभी पैरामीटर क्या हैं और उनका क्या मतलब है। आप DataTypeTwoParameters constructor में समझदार चूक भी प्रदान कर सकते हैं ताकि केवल उन्हीं मानों को सेट किया जा सके जो किसी भी क्रम में API के उपभोक्ता को पसंद आए।


दिलचस्प दृष्टिकोण। आप एक प्रासंगिक कहां रखेंगे Integer.parseInt? एक सेटर में, या पैरामीटर वर्ग के बाहर?
डुर्रोन 597

5
पैरामीटर वर्ग के बाहर। पैरामीटर वर्ग एक "गूंगा" ऑब्जेक्ट होना चाहिए, और एक्सप्रेस के अलावा कुछ भी करने की कोशिश नहीं करनी चाहिए जो आवश्यक इनपुट और उनके प्रकार हैं। पार्सिंग को अन्यत्र किया जाना चाहिए, जैसे p.bar = Integer.parseInt("4"):।
एरिक

7
यह एक पैरामीटर ऑब्जेक्ट पैटर्न की तरह लगता है
gnat

9
... या विरोधी पैटर्न।
तेलस्टिन

1
... या आप केवल नाम बदल DataTypeTwoParametersसकते हैं DataTypeTwo
user253751

14

आपके पास वास्तव में दो अलग-अलग चिंताएं हैं: एक एपीआई लपेटना और तर्क गिनती कम रखना।

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

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

  • एरिक के जवाब में एक पैरामीटर ऑब्जेक्ट, जैसा कि ।
  • बिल्डर पैटर्न है, जहां आप एक अलग बिल्डर वस्तु बनाने, तो अलग-अलग पैरामीटर सेट करने के लिए है, तो अपने अंत वस्तु बनाने setters के एक नंबर पर कॉल करें।
  • प्रोटोटाइप पैटर्न है, जहां आप अपने वांछित वस्तु की उपवर्गों क्लोन के साथ क्षेत्रों को पहले से ही आंतरिक रूप से निर्धारित किया है।
  • एक कारखाना, जिसके बारे में आप पहले से ही परिचित हैं।
  • उपरोक्त में से कुछ संयोजन।

ध्यान दें कि ये रचनात्मक पैटर्न अक्सर एक बहुपदीय रचनाकार को बुलाते हैं, जिसे आपको ठीक होना चाहिए, जब यह संक्षिप्त हो। Polyadic constructors के साथ समस्या उन्हें एक बार कॉल करने की नहीं है, यह तब होता है जब आप उन्हें हर बार कॉल करने के लिए मजबूर करते हैं जब आपको ऑब्जेक्ट बनाने की आवश्यकता होती है।

ध्यान दें कि आमतौर पर OurDataवस्तु के संदर्भ को संदर्भित करके और इसके कॉल को आगे बढ़ाने के बजाय, अपने इनरल्स को फिर से लागू करने की कोशिश करने के लिए अंतर्निहित एपीआई से गुजरना बहुत आसान और अधिक बनाए रखने योग्य है । उदाहरण के लिए:

public class DataTypeTwo implements DataInterface {
  private OurData data;

  public DataTypeOne(OurData data) {
    this.data = data;
  }

   public String getFoo() {
    return data.getFoo();
  }

  public int getBar() {
    return Integer.parseInt(data.getBar());
  }
  ...
}

इस उत्तर का पहला भाग: महान, बहुत सहायक, +1। इस उत्तर का दूसरा भाग: " OurDataऑब्जेक्ट के लिए एक संदर्भ संग्रहीत करके अंतर्निहित एपीआई से गुजरना " - यह वही है जो मैं बचने की कोशिश कर रहा हूं, कम से कम आधार वर्ग में, यह सुनिश्चित करने के लिए कि कोई निर्भरता नहीं है।
डुर्रोन 597

1
इसलिए आप इसे अपने कार्यान्वयन में से एक में ही करते हैं DataInterface। आप अपनी नकली वस्तुओं के लिए एक और कार्यान्वयन बनाते हैं।
कार्ल

@ durron597: हाँ, लेकिन आप पहले से ही जानते हैं कि उस समस्या को कैसे हल किया जाए यदि यह वास्तव में आपको परेशान करता है।
Doc Brown

1

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

आप पैरामीटर ऑब्जेक्ट पैटर्न का उपयोग कर सकते हैं, जैसा कि एक टिप्पणी में सुझाया गया है, आप के लिए इन निर्माण मापदंडों को लपेट सकते हैं, आपके स्थानीय डेटा प्रकार के आवरण पहले से ही , अनिवार्य रूप से, एक पैरामीटर वस्तु है। आपकी सभी Parameter ऑब्जेक्ट क्या कर रही है, पैरामीटर को कैसे पैक कर रही है (आप इसे कैसे बनाएंगे? एक polyadic constructor के साथ?) और फिर उन्हें दूसरी बार किसी ऑब्जेक्ट में अनपैक करते हुए, जो लगभग समान है।

यदि आप अपने खेतों के लिए बसने को उजागर नहीं करना चाहते हैं और उन्हें कॉल करना चाहते हैं, तो मुझे लगता है कि एक अच्छी तरह से परिभाषित और इनकैप्सुलेटेड फैक्ट्री के अंदर एक पॉलैडिक कंस्ट्रक्टर से चिपकना ठीक है।


समस्या यह है, मेरी डेटा संरचना में फ़ील्ड्स की संख्या कई बार बदल गई है और शायद फिर से बदल जाएगी। जिसका मतलब है कि मुझे अपने सभी परीक्षण मामलों में कंस्ट्रक्टर को फिर से तैयार करना होगा। समझदार चूक के साथ पैरामीटर पैटर्न जाने के लिए एक बेहतर तरीका लगता है; एक परिवर्तनशील संस्करण है जो अपरिवर्तनीय रूप में सहेजा जाता है, मेरे जीवन को कई तरीकों से आसान बना सकता है।
डुर्रोन 597
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.