2012-03-16 14 views
5

मैं सिंगलटन पैटर्न के नुकसान के बारे में reading था। कई मंचों में सुझाए गए सिंगलटन का वैध उपयोग लॉगिंग एप्लिकेशन है। मैं सोच रहा था कि यह पैटर्न का वैध उपयोग क्यों है। क्या हम पूरे आवेदन में स्मृति में राज्य की जानकारी को बनाए रखते नहीं हैं?एप्लिकेशन लॉगिंग के लिए सिंगलटन का उपयोग क्यों करें?

क्यों सिर्फ एक समारोह का उपयोग नहीं:

class Logger 
{ 
    public static void Log(string message) 
    { 
     //Append to file 

    } 
} 

उत्तर

2

यह बेहतर है इंटरफ़ेस घोषित करने के लिए:

interface ILogger 
{ 
    public void Log(string message); 
} 

तो लकड़हारा

class FileLogger : ILogger 
{ 
    public void Log(string message) 
    { 
     //Append to file 
    } 
} 

class EmptyLogger : ILogger 
{ 
    public void Log(string message) 
    { 
     //Do nothing 
    } 
} 

के विशिष्ट प्रकार को लागू करने और जहां आवश्यक इंजेक्षन। परीक्षण में आप EmptyLogger इंजेक्ट करेंगे। सिंगलटन का उपयोग करना कठिन बना देगा, क्योंकि आपको परीक्षण में फ़ाइल में भी सहेजना होगा। यदि आप परीक्षण करना चाहते हैं कि कक्षा सही लॉग प्रविष्टियां बनाती है, तो आप नकली का उपयोग कर सकते हैं और अपेक्षाओं को परिभाषित कर सकते हैं।

इंजेक्शन के बारे में:

public class ClassThatUsesLogger 
{ 
    private ILogger Logger { get; set; } 
    public ClassThatUsesLogger(ILogger logger) { Logger = logger } 
} 

ClassThatUsesLogger उत्पादन कोड में FileLogger लेता है:

classThatUsesLogger = new ClassThatUsesLogger(new FileLogger()); 

परीक्षणों में यह EmptyLogger लेता है:

classThatUsesLogger = new ClassThatUsesLogger(new EmptyLogger()); 

आप विभिन्न परिदृश्यों में विभिन्न वालों इंजेक्षन। इंजेक्शन को संभालने के बेहतर तरीके हैं, लेकिन आपको कुछ पढ़ना होगा।

संपादित

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

+0

मैं इंजेक्षन हिस्सा समझ में नहीं आया। क्या आप विस्तार से बता सकते हैं? – Nemo

+0

एक वर्ग जिसके लिए लॉगर की आवश्यकता होती है, लेकिन लॉगर नहीं बनाता है, एक वर्ग के निर्माण के दौरान पारित किया गया है (या आमतौर पर कन्स्ट्रक्टर के माध्यम से) को कक्षा में लॉगर * इंजेक्शन * के रूप में जाना जाता है। http://en.wikipedia.org/wiki/Dependency_injection –

1

मुझे यकीन है कि तुम क्या जब आप स्मृति में शेष राज्य सूचना के बारे में पूछने के लिए बात कर रहे हैं नहीं कर रहा हूँ, लेकिन एक कारण प्रवेश के लिए स्थिर से अधिक सिंगलटन के पक्ष में है कि सिंगलटन अभी भी करने के लिए दोनों
(1) कार्यक्रम करने की अनुमति देता है abstractions (ILogger) और
(2) निर्भरता इंजेक्शन का अभ्यास करके निर्भरता उलटा सिद्धांत का पालन करें।

आप (जब तक आप Action<string> हर जगह की तरह कुछ पास करना चाहते हैं) एक निर्भरता के रूप में अपने स्थिर प्रवेश विधि इंजेक्षन नहीं कर सकते, लेकिन आप एक सिंगलटन वस्तु पारित कर सकते हैं, और आप NullLogger की तरह अलग अलग कार्यान्वयन पारित कर सकते हैं जब इकाई परीक्षण लेखन ।

3

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

0

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

+0

और मैं @Jay और LukLed से सहमत हूं कि आपको नियंत्रण में उलटा होने की अनुमति देने के लिए अपने लॉगर को एक इंटरफ़ेस के रूप में परिभाषित करना चाहिए। यह आपके परीक्षण को अधिक आसान बना देगा। –

1

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

लॉगिंग उन परिस्थितियों में से एक नहीं है। ऐसा इसलिए है क्योंकि लॉगिंग आपके कोड के निष्पादन को प्रभावित नहीं करती है। http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html: जो है, जैसा कि यहां बताया

आपके आवेदन किसी भी अलग किया जाए या नहीं एक दिया लकड़हारा सक्षम किया गया है व्यवहार नहीं करता। यहां दी गई जानकारी एक तरफ बहती है: आपके लॉगर में एप्लिकेशन से।

हालांकि आप शायद अभी भी सिंगलटन पैटर्न का उपयोग नहीं करना चाहते हैं। कम से कम नहीं। ऐसा इसलिए है क्योंकि लॉगर के एक उदाहरण को बल देने का कोई कारण नहीं है। क्या होगा यदि आप दो लॉग फाइलें चाहते थे, या दो लॉगर्स जो अलग-अलग व्यवहार करते थे और विभिन्न उद्देश्यों के लिए उपयोग किए जाते थे?

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

  1. आसान तरीका बस अपने आवेदन में एक स्थिर क्षेत्र है कि लकड़हारा के कहने शामिल है:

    public final static LOGGER = new Logger(); 
    
  2. या यदि आपके लकड़हारा एक फैक्टरी द्वारा बनाई गई है:

    public final static LOGGER = new LoggerFactory().getLogger("myLogger"); 
    
  3. या यदि आपका लॉगर डी कंटेनर द्वारा बनाया गया है:

    public final static LOGGER = Container.getInstance("myLogger"); 
    
  4. आप अपने लॉगर कार्यान्वयन को कॉन्फ़िगर करने योग्य बना सकते हैं, या तो कॉन्फ़िगरेशन फ़ाइल के माध्यम से, जब आप परीक्षण कर रहे हों तो आप "मोड = टेस्ट" पर सेट कर सकते हैं, ताकि उन मामलों में लॉगर तदनुसार व्यवहार कर सके, या तो लॉगिंग न करें , या कंसोल पर लॉगिंग।

    public final static LOGGER = new Logger("logConfig.cfg"); 
    
  5. तुम भी लकड़हारा के व्यवहार रनटाइम पर कॉन्फ़िगर करने योग्य होना बना सकता है। तो जब परीक्षण चलते हैं तो आप इसे आसानी से सेट कर सकते हैं: LOGGER.setMode ("test");

  6. या यदि आप स्थिर अंतिम नहीं बनाते हैं, तो आप अपने परीक्षण के सेटअप में एक परीक्षण लॉगर या मॉक लॉगर के साथ स्थिर LOGGER को प्रतिस्थापित कर सकते हैं।

  7. कुछ थोड़ा अधिक सजावटी आप ऐसा कर सकते एक सिंगलटन पैटर्न के करीब है लेकिन काफी नहीं है:

    public class Logger 
    { 
        private static Logger default; 
        public static getDefault() 
        { 
         if(default == null) 
         { 
          throw new RuntimeException("No default logger was specified."); 
         } 
         return default; 
        } 
    
        public static void setDefault(Logger logger) 
        { 
         if(default != null) 
         { 
          throw new RuntimeException("Default logger already specified."); 
         } 
         default = logger; 
        } 
    
        public Logger() 
        { 
        } 
    } 
    
    public static void main(String [] args) 
    { 
        Logger.setDefault(new Logger()); 
    } 
    
    @Test 
    public void myTest() 
    { 
        Logger.setDefault(new MockedLogger()); 
    
        // ... test stuff 
    } 
    
संबंधित मुद्दे