स्टेटिक फैक्ट्री बनाम फैक्ट्री एक सिंगलटन के रूप में


24

मेरे कुछ कोड में, मेरे पास इसके समान एक स्थिर कारखाना है:

public class SomeFactory {

    // Static class
    private SomeFactory() {...}

    public static Foo createFoo() {...}

    public static Foo createFooerFoo() {...}
}

एक कोड समीक्षा के दौरान, यह प्रस्तावित किया गया था कि यह एक सिंगलटन और इंजेक्शन होना चाहिए। तो, यह इस तरह दिखना चाहिए:

public class SomeFactory {

    public SomeFactory() {}

    public Foo createFoo() {...}

    public Foo createFooerFoo() {...}
}

हाइलाइट करने के लिए कुछ बातें:

  • दोनों कारखाने स्टेटलेस हैं।
  • विधियों के बीच एकमात्र अंतर उनके स्कोप (उदाहरण बनाम स्थिर) हैं। कार्यान्वयन समान हैं।
  • फू एक सेम है जिसमें एक इंटरफ़ेस नहीं है।

स्थिर होने के लिए मेरे पास तर्क थे:

  • वर्ग सांविधिक है, इसलिए उसे तत्काल करने की आवश्यकता नहीं है
  • ऐसा प्रतीत होता है कि किसी कारखाने को इंस्टेंट करने की तुलना में एक स्थिर विधि को कॉल करने में सक्षम होना स्वाभाविक है

एक एकल के रूप में कारखाने के लिए तर्क थे:

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

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

समुदाय क्या सोचता है? जबकि मुझे सिंगलटन दृष्टिकोण पसंद नहीं है, मुझे इसके खिलाफ बहुत मजबूत तर्क नहीं लगता है।


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

तो, क्या आप सामान्य, या सिर्फ स्थिर कारखानों में स्थिर उपयोगिताओं के खिलाफ वकालत कर रहे हैं?

1
मैं उनके खिलाफ सामान्य रूप से वकालत कर रहा हूं। उदाहरण के लिए C DateTimeऔर Fileकक्षाओं में कुख्यात रूप से समान कारणों से परीक्षण करना कठिन है। यदि आपके पास उदाहरण के लिए एक वर्ग है जो निर्माणकर्ता में Createdतारीख निर्धारित करता है DateTime.Nowतो आप इनमें से दो वस्तुओं के साथ एक यूनिट टेस्ट बनाने के बारे में कैसे जाते हैं जो 5 मिनट अलग बनाए गए थे? क्या वर्षों के अलावा? आप वास्तव में ऐसा नहीं कर सकते (बहुत काम के बिना)।
माइक

2
क्या आपकी सिंगलटन फैक्ट्री में privateकंस्ट्रक्टर और कोई getInstance()विधि नहीं होनी चाहिए ? क्षमा करें, अमिट नाइट-पिकर!
TMN

1
@ माइक - सहमत - मैंने अक्सर एक DateTimeProvider वर्ग का उपयोग किया है, जो कि DateTime.Now लौटाता है। फिर आप इसे एक मॉक के लिए स्वैप कर सकते हैं जो आपके परीक्षणों में पूर्व-निर्धारित समय देता है। यह सभी कंस्ट्रक्टर्स को पास करने के लिए अतिरिक्त काम है - सबसे बड़ा सिरदर्द है जब सहकर्मी इसे 100% में नहीं खरीदते हैं और DateTime के स्पष्ट संदर्भ को पर्ची करते हैं। आलस्य से बाहर ... :)
जूलिया हेवर्ड

जवाबों:


15

आप अपने कारखाने को ऑब्जेक्ट-प्रकार से अलग क्यों करेंगे जो इसे बनाता है?

public class Foo {

    // Private constructor
    private Foo(...) {...}

    public static Foo of(...) { return new Foo(...); }
}

यह बात जोशुआ बलोच ने अपनी पुस्तक "प्रभावी जावा" के पृष्ठ 5 पर अपने आइटम 1 के रूप में वर्णित की है। जावा अतिरिक्त कक्षाओं और एकल और whatnot को जोड़े बिना पर्याप्त है। कुछ उदाहरण हैं जहां एक अलग कारखाना वर्ग समझ में आता है, लेकिन वे कुछ और दूर हैं।

रचनाकारों के विपरीत, जोशुआ बलोच के आइटम 1 को परिमार्जित करने के लिए, स्थैतिक कारखाने विधियां कर सकते हैं:

  • ऐसे नाम हैं जो विशिष्ट प्रकार की वस्तु निर्माण का वर्णन कर सकते हैं (उदाहरण के रूप में बलोच संभावित उपयोग करता है (int, int, random)
  • एक मौजूदा वस्तु लौटें (विचार करें: फ्लाईवेट)
  • मूल वर्ग का एक उप-प्रकार या एक इंटरफ़ेस लौटाएं जो इसे लागू करता है
  • वर्बोसिटी कम करें (निर्दिष्ट प्रकार के मापदंडों में - उदाहरण के लिए बलोच देखें)

नुकसान:

  • सार्वजनिक या संरक्षित निर्माता के बिना वर्ग को उपवर्गित नहीं किया जा सकता है (लेकिन आप संरक्षित निर्माणकर्ताओं और / या आइटम 16 का उपयोग कर सकते हैं: विरासत के अनुकूल रचना)
  • स्थैतिक कारखाने के तरीके अन्य स्थिर तरीकों से बाहर नहीं खड़े होते हैं (जैसे मानक नाम () या valueOf () का उपयोग करें)

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


मैंने इसके बारे में भी सोचा है, और मुझे लगता है कि मुझे यह पसंद है। मुझे याद नहीं है कि मैंने उन्हें पहले स्थान पर क्यों अलग किया।
bstempi

सामान्यतया, हम बलोच की शैली पर सहमत होते हैं। अंततः, हम अपने कारखाने के तरीकों को कक्षा में स्थानांतरित करने जा रहे हैं जो वे तात्कालिक हैं। हमारे समाधान और आपके बीच एकमात्र अंतर यह है कि कंस्ट्रक्टर सार्वजनिक रहेगा, ताकि लोग अभी भी क्लास को सीधे कर सकें। आपके उत्तर के लिए धन्यवाद!
21

लेकिन ... यह भयानक है। आप सबसे अधिक संभावना एक वर्ग चाहते हैं जो IFoo को लागू करता है, और क्या यह पास हुआ है। इस पैटर्न का उपयोग करना, यह अब संभव नहीं है; उदाहरणों को प्राप्त करने के लिए आपको फू को मतलब करने के लिए IFoo को हार्डकोड करना होगा। यदि आपके पास IFooFactory है जिसमें एक IFoo create () है, तो क्लाइंट कोड को कार्यान्वयन के विवरण को जानने की जरूरत नहीं है कि कौन सा Foo IFoo के रूप में चुना गया था।
विल्बर्ट

@Wilbert public static IFoo of(...) { return new Foo(...); } समस्या क्या है? बलोच इस डिजाइन के फायदों में से एक के रूप में आपकी चिंता को संतुष्ट करता है (ऊपर दूसरा बुलेट पॉइंट)। मुझे आपकी टिप्पणी समझ में नहीं आ रही होगी।
ग्लेनपेटर्सन

एक उदाहरण बनाने के लिए, यह डिज़ाइन उम्मीद करता है: Foo.of (...)। हालांकि, फू के अस्तित्व को कभी भी उजागर नहीं किया जाना चाहिए, केवल IFoo को जाना जाना चाहिए (जैसा कि फू है लेकिन IFoo का एक ठोस निहितार्थ है)। कंक्रीट पर एक स्थिर फैक्टरी विधि होने से यह टूट जाता है और क्लाइंट कोड और कंक्रीट कार्यान्वयन के बीच एक युग्मन हो जाता है।
विल्बर्ट

5

मेरे सिर के ठीक ऊपर, यहाँ जावा में स्टेटिक्स के साथ कुछ समस्याएं हैं:

  • स्थिर तरीके OOP "नियमों" से नहीं खेलते हैं। आप किसी वर्ग को विशिष्ट स्थिर विधियों (जहाँ तक मुझे पता है) को लागू करने के लिए बाध्य नहीं कर सकते। इसलिए आपके पास एक ही हस्ताक्षर के साथ स्थिर विधियों के एक ही सेट को लागू करने वाली कई कक्षाएं नहीं हो सकती हैं। इसलिए आप जो भी कर रहे हैं, उसमें से एक के साथ फंस गए हैं। आप वास्तव में एक स्थिर वर्ग को उपवर्ग नहीं कर सकते। तो कोई बहुरूपता, आदि।

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

  • स्टैटिक्स फ़ील्ड ग्लोबल्स के समान सुंदर हैं


आपने उल्लिखित किया था:

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

जो मुझे आपसे पूछने के लिए उत्सुक बनाता है:

  • आपकी राय में, स्थिर तरीकों के क्या फायदे हैं?
  • क्या आप मानते हैं कि StringUtilsसही ढंग से डिजाइन और कार्यान्वित किया गया है?
  • आपको क्या लगता है कि आपको कारखाने का मज़ाक उड़ाने की ज़रूरत नहीं होगी?

हे, उत्तर के लिए धन्यवाद! आपके द्वारा पूछे गए क्रम में: (1) स्थिर विधियों के लाभ वाक्यात्मक चीनी और अनावश्यक आवृत्ति की कमी है। ऐसा कोड पढ़ना अच्छा है जिसमें स्टैटिक इम्पोर्ट है और Foo f = createFoo();नाम उदाहरण के बजाय कुछ कहना है । उदाहरण ही कोई उपयोगिता नहीं प्रदान करता है। (२) मुझे ऐसा लगता है। यह इस तथ्य को दूर करने के लिए डिज़ाइन किया गया था कि उन तरीकों में से कई Stringकक्षा के भीतर मौजूद नहीं हैं । मूलभूत के रूप Stringमें कुछ को बदलना एक विकल्प नहीं है, इसलिए बर्तन जाने का रास्ता है। (cont'd)
bstempi

वे उपयोग करने के लिए आसान हैं और आपको किसी भी उदाहरण के आसपास डरने की आवश्यकता नहीं है। वे स्वाभाविक महसूस करते हैं। (३) क्योंकि मेरी फैक्ट्री बहुत हद तक फैक्ट्री के तरीकों से मिलती-जुलती है Integer। वे बहुत सरल हैं, बहुत कम तर्क हैं, और केवल सुविधा प्रदान करने के लिए हैं (उदाहरण के लिए, उनके पास कोई अन्य कारण नहीं है)। मेरे तर्क का मुख्य हिस्सा यह है कि वे बिना किसी राज्य पर भरोसा करते हैं - परीक्षणों के दौरान उन्हें अलग करने का कोई कारण नहीं है। StringUtilsपरीक्षण करते समय लोग मजाक नहीं उड़ाते हैं - उन्हें इस सरल सुविधा कारखाने का मजाक उड़ाने की आवश्यकता क्यों होगी?
19

3
वे मजाक नहीं करते हैं StringUtilsक्योंकि एक उचित आश्वासन है कि वहां के तरीकों का कोई दुष्प्रभाव नहीं है।
रॉबर्ट हार्वे

@RobertHarvey मुझे लगता है कि मैं अपने कारखाने के बारे में एक ही दावा कर रहा हूं - यह कुछ भी संशोधित नहीं करता है। जैसे StringUtilsइनपुट्स को बदले बिना कुछ परिणाम देता है , वैसे ही मेरा कारखाना भी कुछ भी संशोधित नहीं करता है।
19

@bstempi मैं वहां सुकराती पद्धति के लिए जा रहा था। मुझे लगता है कि मैं इसे चूसता हूं।

0

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

कल्पना करें कि आपका एप्लिकेशन विकसित होता है और अब कुछ उदाहरणों में आपको FooBar(फू का उपवर्ग) बनाने की आवश्यकता है। अब आपको अपने कोड में उन सभी जगहों से गुजरने की ज़रूरत है जो आपके बारे में जानते हैं SomeFactoryऔर किसी प्रकार के तर्क को लिखते हैं जो देखता है या someConditionकॉल करता है SomeFactoryया SomeOtherFactory। दरअसल, आपके उदाहरण कोड को देखकर, यह उससे भी बदतर है। आप अलग-अलग Foos बनाने के लिए विधि के बाद सिर्फ विधि जोड़ने की योजना बनाते हैं और आपके सभी क्लाइंट कोड को यह पता लगाने से पहले एक शर्त की जांच करनी होती है कि कौन सा Foo बनाना है। जिसका अर्थ है कि सभी ग्राहकों को इस बात का गहन ज्ञान होना चाहिए कि आपका कारखाना कैसे काम करता है और जब भी कोई नया फू (या हटाया जाता है!) उन्हें बदलने की आवश्यकता होती है।

अब कल्पना करें कि क्या आपने अभी एक IFooFactoryऐसा बनाया है जो एक बनाता है IFoo। अब, एक अलग उपवर्ग या यहां तक ​​कि एक पूरी तरह से अलग कार्यान्वयन का उत्पादन बच्चे का खेल है - आप सिर्फ सही IFooFactory में खिलाते हैं श्रृंखला ऊपर और फिर जब ग्राहक इसे प्राप्त करता है, तो यह कॉल करता है gimmeAFoo()और सही फू प्राप्त करेगा क्योंकि इसे सही कारखाना मिला ।

आप सोच रहे होंगे कि यह उत्पादन कोड में कैसे खेलता है। कोडबेस से आपको एक उदाहरण देने के लिए, मैं उस प्रोजेक्ट पर काम कर रहा हूं, जो परियोजनाओं को क्रैंक करने के सिर्फ एक साल के बाद शुरू करना है, मेरे पास फैक्ट्रियों का एक समूह है जो प्रश्न डेटा ऑब्जेक्ट बनाने के लिए उपयोग किए जाते हैं (वे प्रस्तुति मॉडल की तरह कुछ हैं )। मेरे पास QuestionFactoryMapमूल रूप से यह देखने के लिए एक हैश है कि प्रश्न कारखाने का उपयोग कब करना है। इसलिए एक एप्लिकेशन बनाने के लिए जो अलग-अलग प्रश्न पूछता है, मैंने अलग-अलग फैक्ट्रियों को मानचित्र में रखा।

प्रश्न निर्देश या अभ्यास (जो दोनों को लागू करते हैं IContentBlock) में प्रस्तुत किए जा सकते हैं , इसलिए प्रत्येक InstructionsFactoryया ExerciseFactoryQuestionFactoryMap की एक प्रति प्राप्त करेंगे। फिर, विभिन्न निर्देशों और व्यायाम कारखानों को सौंप दिया जाता है ContentBlockBuilderजो फिर से एक हैश रखता है जिसका उपयोग कब करना है। और फिर जब XML में लोड किया जाता है, तो ContentBlockBuilder, हैश से एक्सरसाइज या निर्देश फैक्टरी को कॉल करता है और इसके लिए पूछता है createContent(), और फिर फैक्ट्री प्रश्नों के निर्माण को दर्शाता है , जो भी इसके आंतरिक प्रश्न-चिह्न से बाहर खींचता है, उसके आधार पर प्रत्येक XML नोड बनाम हैश में है। दुर्भाग्य से , यह बात एक के BaseQuestionबजाय एक हैIQuestion, लेकिन यह सिर्फ यह दिखाने के लिए जाता है कि जब आप डिजाइन के बारे में सावधान हो रहे हैं तब भी आप ऐसी गलतियाँ कर सकते हैं जो आपको लाइन से नीचे कर सकती हैं।

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


Integerकक्षा में कारखाने के तरीकों के एक पल के लिए सोचें - एक स्ट्रिंग से परिवर्तित करने जैसी सरल चीजें। वही Fooकरता है। मेरा Fooमतलब विस्तारित होना नहीं है (यह बताते हुए मेरी गलती नहीं है), इसलिए मेरे पास आपके बहुरूपी मुद्दे नहीं हैं। मैं बहुरूपी कारखानों के मूल्य को समझता हूं और अतीत में उनका उपयोग कर चुका हूं ... मुझे नहीं लगता कि वे यहां उपयुक्त हैं। क्या आप कल्पना कर सकते हैं कि यदि आपको किसी कारखाने को तत्काल चालू करना है, तो उस पर Integerस्थिर कारखानों के माध्यम से वर्तमान में बेक किया जा सकता है?
bstempi

इसके अलावा, आप आवेदन के बारे में मान्यताओं बहुत दूर हैं। आवेदन काफी शामिल है। यह Foo, हालांकि, कई वर्गों की तुलना में बहुत सरल है। यह सिर्फ एक साधारण POJO है।
bstempi

यदि फू को विस्तारित करने का मतलब है, तो वह अधिकार फैक्टरी के उदाहरण के लिए एक कारण है - आप if (this) then {makeThisFoo()} else {makeThatFoo()}अपने कोड के माध्यम से सभी को कॉल करने के बजाय आपको सही उपवर्ग देने के लिए कारखाने का सही उदाहरण प्रदान करते हैं । एक बात जो मैंने बुरी तरह से खराब वास्तुकला वाले लोगों के बारे में देखी है, वह यह है कि वे पुराने दर्द में लोगों की तरह हैं। वे वास्तव में यह नहीं जानते हैं कि ऐसा क्या लगता है कि दर्द में नहीं होना है, इसलिए वे जिस दर्द में हैं, वह सब बुरा नहीं लगता है।
एमी ब्लेंकशिप 12

इसके अलावा, आप स्थैतिक तरीकों (यहां तक ​​कि मठ वाले!) के साथ समस्याओं के बारे में इस लिंक की जांच करना चाहते हैं
एमी ब्लेंकशिप

मैंने सवाल अपडेट किया है। आप और एक अन्य व्यक्ति ने विस्तार का उल्लेख किया Foo। ठीक ही तो - मैंने इसे अंतिम घोषित नहीं किया। Fooएक बीन है जिसे विस्तारित करने के लिए नहीं है।
bstempi
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.