क्या एक एसआरपी उल्लंघन के कार्यान्वयन के बगल में लॉगिंग है?


19

जब चुस्त सॉफ्टवेयर विकास और सभी सिद्धांतों (एसआरपी, ओसीपी, ...) के बारे में सोच रहा हूं तो मैं खुद से पूछता हूं कि लॉगिंग का इलाज कैसे करें।

क्या एक एसआरपी उल्लंघन के कार्यान्वयन के बगल में लॉगिंग है?

मैं कहूंगा yesक्योंकि क्रियान्वयन भी बिना लाग-लपेट के चलने में सक्षम होना चाहिए। तो मैं बेहतर तरीके से लॉगिंग कैसे लागू कर सकता हूं? मैंने कुछ पैटर्न की जाँच की है और एक निष्कर्ष पर आया है कि उपयोगकर्ता-परिभाषित तरीके से सिद्धांतों का उल्लंघन नहीं करने का सबसे अच्छा तरीका है, लेकिन किसी भी पैटर्न का उपयोग करने के लिए जो सिद्धांत का उल्लंघन करने के लिए जाना जाता है, एक डेकोरेटर पैटर्न का उपयोग करना है।

मान लीजिए कि हमारे पास एसआरपी उल्लंघन के बिना पूरी तरह से घटकों का एक गुच्छा है और फिर हम लॉगिंग जोड़ना चाहते हैं।

  • घटक ए
  • घटक B A का उपयोग करता है

हम ए के लिए लॉगिंग करना चाहते हैं, इसलिए हम एक अन्य घटक डी बनाते हैं, जिसे ए I दोनों को लागू करने के साथ सजाया गया है।

  • इंटरफ़ेस मैं
  • घटक L (सिस्टम का लॉगिंग घटक)
  • घटक एक उपकरण I
  • घटक डी लागू करता है I, सजाता है / ए का उपयोग करता है, लॉगिंग के लिए एल का उपयोग करता है
  • घटक B एक I का उपयोग करता है

लाभ: - मैं बिना लॉग-इन के ए का उपयोग कर सकता हूं - ए का परीक्षण करने का मतलब है कि मुझे किसी लॉगिंग मोक्स की आवश्यकता नहीं है - परीक्षण सरल हैं

नुकसान: - अधिक घटक और अधिक परीक्षण

मुझे पता है कि यह एक और खुला चर्चा प्रश्न लगता है, लेकिन मैं वास्तव में जानना चाहता हूं कि क्या कोई डेकोरेटर या एसआरपी उल्लंघन की तुलना में बेहतर लॉगिंग रणनीतियों का उपयोग करता है। स्टैटिक सिंगलटन लॉगर के बारे में क्या है जो डिफॉल्ट NullLogger के समान है और यदि syslog- लॉगिंग चाहता है, तो कोई रनटाइम पर कार्यान्वयन ऑब्जेक्ट को बदल देता है?



मैंने पहले ही इसे पढ़ लिया है और उत्तर संतोषजनक नहीं है, क्षमा करें।
ऐच


@MarkRogers उस दिलचस्प लेख को साझा करने के लिए धन्यवाद। अंकल बॉब 'क्लीन कोड' में कहते हैं, एक अच्छा एसआरपी घटक अमूर्त के समान स्तर पर अन्य घटकों के साथ काम कर रहा है। मेरे लिए यह स्पष्टीकरण समझना आसान है क्योंकि संदर्भ बहुत बड़ा हो सकता है। लेकिन मैं इस सवाल का जवाब नहीं दे सकता, क्योंकि एक लकड़हारा के संदर्भ या अमूर्तता क्या है?
ऐच

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

जवाबों:


-1

हां यह एसआरपी का उल्लंघन है क्योंकि लॉगिंग एक क्रॉस कटिंग चिंता का विषय है।

सही तरीका यह है कि आप लॉगिंग क्लास (इंटरसेप्शन) में लॉगिंग को डेलिगेट करें, जिसका एकमात्र उद्देश्य एसआरपी द्वारा लॉग-इन करना है।

इस लिंक को एक अच्छे उदाहरण के लिए देखें: https://msdn.microsoft.com/en-us/library/dn178467%28v=pandp.30%29.aspx

यहाँ एक छोटा उदाहरण दिया गया है :

public interface ITenantStore
{
    Tenant GetTenant(string tenant);
    void SaveTenant(Tenant tenant);
}

public class TenantStore : ITenantStore
{
    public Tenant GetTenant(string tenant)
    {....}

    public void SaveTenant(Tenant tenant)
    {....}
} 

public class TenantStoreLogger : ITenantStore
{
    private readonly ILogger _logger; //dep inj
    private readonly ITenantStore _tenantStore;

    public TenantStoreLogger(ITenantStore tenantStore)
    {
        _tenantStore = tenantStore;
    }

    public Tenant GetTenant(string tenant)
    {
        _logger.Log("reading tenant " + tenant.id);
        return _tenantStore.GetTenant(tenant);
    }

    public void SaveTenant(Tenant tenant)
    {
        _tenantStore.SaveTenant(tenant);
        _logger.Log("saving tenant " + tenant.id);
    }
}

लाभ शामिल हैं

  • आप इसका परीक्षण बिना लॉग-इन किए कर सकते हैं - सच्ची इकाई परीक्षण
  • आप आसानी से लॉगिंग चालू / बंद कर सकते हैं - यहां तक ​​कि रनटाइम पर भी
  • आप लॉगिंग के अन्य रूपों के लिए लॉगिंग को स्थानापन्न कर सकते हैं, बिना टेनेंटस्टोर फ़ाइल को कभी भी बदलने के लिए।

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

2
मैं आपको कहीं भी _logger चर असाइन करने के लिए नहीं देखता। क्या आप कंस्ट्रक्टर इंजेक्शन का उपयोग करने की योजना बना रहे थे और बस भूल गए थे? यदि ऐसा है, तो आप शायद एक संकलक चेतावनी प्राप्त करेंगे।
user2023861

27
टेनेंटस्टोर के बजाय एक सामान्य उद्देश्य लकड़हारे के साथ डुबकी लगाई जा रही है, जिसके लिए एन + 1 कक्षाएं (जब आप एक मकान मालिक, एक फ़्यूस्टोर, एक बारस्टोर इत्यादि जोड़ते हैं) की आवश्यकता होती है, तो आपके पास एक टेनेंटस्टॉर लॉगर है जिसे टेनंटस्टॉर के साथ डुबकी दी जा रही है, एक फूस्टस्टोर लॉगर के साथ डुबकी लगाई जा रही है। , आदि ... 2N वर्गों की आवश्यकता है। जहाँ तक मैं बता सकता हूँ, शून्य लाभ के लिए। जब आप नो-लॉगिंग यूनिट टेस्टिंग करना चाहते हैं, तो आपको केवल NullLogger को कॉन्फ़िगर करने के बजाय, N वर्गों को फिर से जोड़ना होगा। IMO, यह एक बहुत ही खराब दृष्टिकोण है।
user949300

6
ऐसा हर एक वर्ग के लिए करना, जिसे लॉगिंग की बहुत अधिक आवश्यकता है, आपके कोड बेस की जटिलता को बढ़ाता है (जब तक कि कुछ वर्ग लॉगिंग नहीं करते हैं कि आप इसे क्रॉस क्रॉसिंग चिंता भी नहीं कहेंगे)। यह अंतत: कोड को बनाए रखने के लिए बड़ी संख्या में इंटरफेस की वजह से कम बनाए रखता है, जो कि सिंगल रिस्पॉन्सिबिलिटी प्रिंसिपल के लिए बनाई गई हर चीज के खिलाफ जाता है।
jpmc26

9
Downvoted। आपने टेनेंट वर्ग से लॉगिंग चिंता को हटा दिया, लेकिन अब आपका TenantStoreLoggerहर बार TenantStoreपरिवर्तन होगा। आप प्रारंभिक समाधान की तुलना में किसी भी चिंता को अलग नहीं कर रहे हैं।
लॉरेंट ला रिज़्ज़ा

62

मैं कहूंगा कि आप SRP को बहुत गंभीरता से ले रहे हैं। यदि आपका कोड पर्याप्त रूप से स्पष्ट है कि लॉगिंग SRP का एकमात्र "उल्लंघन" है तो आप अन्य सभी प्रोग्रामर के 99% से अधिक बेहतर कर रहे हैं, और आपको अपने आप को पीठ पर थपथपाना चाहिए।

SRP का उद्देश्य भयावह स्पेगेटी कोड से बचना है, जहां कोड जो अलग-अलग काम करता है, सभी को एक साथ मिलाया जाता है। कार्यात्मक कोड के साथ लॉगिंग को मेरे लिए कोई खतरे की घंटी नहीं बजती है।


20
@ ऐच: आपकी पसंद अपनी कक्षा में लॉगिंग को हार्ड-वायर करने के लिए है, एक लॉगर को हैंडल में पास करें या कुछ भी लॉग न करें। यदि आप सब कुछ की कीमत पर SRP के बारे में अल्ट्रा-सख्त होने जा रहे हैं, तो मैं आपको कभी भी कुछ भी लॉग इन नहीं करने की सलाह दूंगा। जो कुछ भी आपको यह जानने की आवश्यकता है कि आपका सॉफ़्टवेयर क्या कर रहा है, डिबगर के साथ बाहर रखा जा सकता है। SRP में P का अर्थ "सिद्धांत," नहीं "प्रकृति का भौतिक नियम है जिसे कभी नहीं तोड़ना चाहिए।"
ब्लरफ्ल

3
@ ऐच: आपको अपनी कक्षा में कुछ आवश्यकता के अनुसार लॉगिंग का पता लगाने में सक्षम होना चाहिए, अन्यथा आप YAGNI का उल्लंघन कर रहे हैं। यदि लॉगिंग टेबल पर है, तो आप एक वैध लकड़हारा हैंडल प्रदान करते हैं, जैसे आप कक्षा की जरूरतों के लिए कुछ भी करेंगे, अधिमानतः एक कक्षा से जो पहले से ही परीक्षण कर चुके हैं। चाहे वह वास्तविक लॉग प्रविष्टियों का उत्पादन करने वाला हो या उन्हें बिट बाल्टी में डंप करने का, यह चिंता का विषय है कि आपकी कक्षा का उदाहरण क्या है; वर्ग को अपनी परवाह नहीं करनी चाहिए
ब्लरफ्ल

3
@ यूनिट परीक्षण के बारे में आपके प्रश्न का उत्तर देने के लिए: Do you mock the logger?यह सटीक है कि आप क्या करते हैं। आपके पास एक ILoggerइंटरफ़ेस होना चाहिए जो लकड़हारे को परिभाषित करता है। परीक्षण के तहत कोड ILoggerआपके द्वारा निर्दिष्ट के साथ इंजेक्ट हो जाता है । परीक्षण के लिए, आपके पास है class TestLogger : ILogger। इसके बारे में बड़ी बात यह है TestLoggerकि पिछली स्ट्रिंग या त्रुटि लॉग जैसी चीजों को उजागर कर सकता है। परीक्षण यह सत्यापित कर सकते हैं कि परीक्षण के तहत कोड सही तरीके से लॉगिंग है। उदाहरण के लिए, एक परीक्षण हो सकता है UserSignInTimeGetsLogged(), जहां परीक्षण TestLoggerलॉग के लिए जांच करता है ।
कर्टिसहैक्स

5
99% थोड़ा कम लगता है। आप शायद सभी प्रोग्रामर के 100% से बेहतर हैं।
पॉल ड्रेपर

2
पवित्रता के लिए +1। हमें इस तरह की सोच की अधिक आवश्यकता है: शब्दों और अमूर्त सिद्धांतों पर कम ध्यान और बनाए रखने योग्य कोड आधार पर अधिक ध्यान केंद्रित करना ।
jpmc26

15

नहीं, यह SRP का उल्लंघन नहीं है।

आपके द्वारा लॉग को भेजे जाने वाले संदेशों को आसपास के कोड के समान कारणों से बदलना चाहिए।

क्या है एसआरपी का उल्लंघन कोड में सीधे लॉगिंग के लिए एक विशिष्ट पुस्तकालय का उपयोग कर रहा है। यदि आप लॉगिंग के तरीके को बदलने का निर्णय लेते हैं, तो एसआरपी कहता है कि यह आपके व्यवसाय कोड को प्रभावित नहीं करना चाहिए।

किसी प्रकार का सार Loggerआपके कार्यान्वयन कोड तक पहुंच योग्य होना चाहिए, और केवल एक चीज जिसे आपके कार्यान्वयन को कहना चाहिए, "इस संदेश को लॉग में भेजें", कोई चिंता नहीं है कि यह कैसे किया गया है। लॉगिंग (यहां तक ​​कि टाइमस्टैम्पिंग) के सटीक तरीके के बारे में निर्णय लेना आपके कार्यान्वयन की जिम्मेदारी नहीं है।

आपके कार्यान्वयन को तब यह भी पता नहीं होना चाहिए कि लकड़हारा इसे संदेश भेज रहा है या नहीं NullLogger

ने कहा कि।

मैं बहुत तेजी से क्रॉस-कटिंग चिंता के रूप में लॉगिंग ब्रश नहीं करेगा । आपके कार्यान्वयन कोड में होने वाली विशिष्ट घटनाओं का पता लगाने के लिए लॉग का उत्सर्जन कार्यान्वयन कोड के अंतर्गत आता है।

ओटीओएच, एक क्रॉस-कटिंग चिंता का विषय है, निष्पादन अनुरेखण है : लॉगिंग प्रवेश करता है और प्रत्येक विधि में बाहर निकलता है। एओपी को ऐसा करने के लिए सबसे अच्छा रखा गया है।


मान लीजिए कि लकड़हारा संदेश 'लॉगिन उपयोगकर्ता xyz' है, जिसे एक लकड़हारे के पास भेजा जाता है जो टाइमस्टैम्प आदि को प्रस्तुत करता है। क्या आप जानते हैं कि कार्यान्वयन के लिए 'लॉगिन' का क्या अर्थ है? क्या यह कुकी या किसी अन्य तंत्र के साथ सत्र शुरू कर रहा है? मुझे लगता है कि एक लॉगिन को लागू करने के कई अलग-अलग तरीके हैं, इस प्रकार परिवर्तन को लागू करने से तार्किक रूप से कोई लेना-देना नहीं है कि एक उपयोगकर्ता लॉग इन करता है। यह अलग-अलग घटकों को सजाने का एक और बढ़िया उदाहरण है (जैसे OAuthLogin, SessionLogin, BasicPuthorizationLogin) एक ही काम कर रहा है। एक ही Loginलकड़हारे के साथ सजाया गया एक इनडर्टफेस के रूप में ।
ऐच

यह निर्भर करता है कि संदेश "लॉगिन उपयोगकर्ता xyz" का क्या अर्थ है। यदि यह इस तथ्य को चिह्नित करता है कि एक लॉगिन सफल है, तो लॉग को संदेश भेजना लॉगिन उपयोग के मामले में है। स्ट्रिंग (OAuth, सत्र, LDAP, NTLM, फिंगरप्रिंट, हम्सटर व्हील) के रूप में लॉगिन जानकारी का प्रतिनिधित्व करने का विशिष्ट तरीका क्रेडेंशियल्स या लॉगिन रणनीति का प्रतिनिधित्व करने वाले विशिष्ट वर्ग में है। इसे हटाने के लिए कोई मजबूर करने की आवश्यकता नहीं है। यह अलग-अलग मामला क्रॉस-कटिंग चिंता का विषय नहीं है। यह लॉगिन उपयोग के मामले के लिए विशिष्ट है।
लॉरेंट ला रिजज़ा

7

जैसा कि लॉगिंग को अक्सर क्रॉस-कटिंग चिंता माना जाता है, मैं सुझाव देता हूं कि एओजी का उपयोग लॉगिंग को कार्यान्वयन से अलग करने के लिए किया जाएगा।

भाषा के आधार पर आप इसे करने के लिए एक इंटरसेप्टर या कुछ AOP फ्रेमवर्क (उदाहरण के लिए जावा में AspectJ) का उपयोग करेंगे।

सवाल यह है कि क्या यह वास्तव में परेशानी के लायक है। ध्यान दें कि यह पृथक्करण बहुत कम लाभ प्रदान करते हुए आपकी परियोजना की जटिलता को बढ़ाएगा।


2
अधिकांश AOP कोड जो मैंने देखा, वह प्रत्येक विधि के हर प्रवेश और निकास चरण में प्रवेश करने के बारे में था। मैं केवल कुछ व्यावसायिक तर्क भागों को लॉग करना चाहता हूं। तो शायद यह केवल एनोटेट तरीकों से लॉग करना संभव है, लेकिन एओपी केवल स्क्रिप्टिंग भाषाओं और वर्चुअल मशीन वातावरण में ही मौजूद हो सकता है, है ना? उदाहरण के लिए C ++ में यह असंभव है। मैं मानता हूं कि मैं एओपी के दृष्टिकोण से बहुत खुश नहीं हूं, लेकिन शायद कोई क्लीनर समाधान नहीं है।
ऐच

1
@Aitch। "सी ++ यह असंभव है।" : यदि आप "aop c ++" के लिए गूगल करते हैं, तो आपको इसके बारे में लेख मिलेंगे। "... मैंने जो AOP कोड देखा था, वह हर विधि के हर प्रवेश और निकास चरण में प्रवेश करने के बारे में था। मैं केवल कुछ व्यावसायिक तर्क भागों को लॉग करना चाहता हूं।" Aop आपको संशोधित करने के तरीकों को खोजने के लिए पैटर्न को परिभाषित करने की अनुमति देता है। नामस्थान से सभी विधियाँ "my.busininess। *"
k3b

1
लॉगिंग अक्सर क्रॉस-कटिंग चिंता का विषय नहीं होता है, खासकर जब आप चाहते हैं कि आपका लॉग दिलचस्प जानकारी हो, यानी अपवाद स्टैक ट्रेस में अधिक जानकारी शामिल है।
लॉरेंट ला रिज़्ज़ा

5

यह ठीक लगता है। आप एक काफी मानक लॉगिंग डेकोरेटर का वर्णन कर रहे हैं। आपके पास:

घटक L (सिस्टम का लॉगिंग घटक)

इसकी एक जिम्मेदारी है: लॉगिंग जानकारी जो इसके पास है।

घटक एक उपकरण I

इसकी एक जिम्मेदारी है: इंटरफ़ेस I का कार्यान्वयन प्रदान करना (यह मानते हुए कि मैं ठीक से SRP-compliant हूं, जो है)।

यह महत्वपूर्ण हिस्सा है:

घटक डी लागू करता है I, सजाता है / ए का उपयोग करता है, लॉगिंग के लिए एल का उपयोग करता है

जब उस तरह से कहा जाता है, तो यह जटिल लगता है, लेकिन इसे इस तरह से देखें: घटक डी एक काम करता है: ए और एल को एक साथ लाना।

  • घटक डी लॉग नहीं करता है; यह एल को दर्शाता है
  • घटक डी खुद को लागू नहीं करता है; यह दर्शाता है कि ए

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

एक महत्वपूर्ण चेतावनी: जब डी आपके लॉगिंग घटक एल का उपयोग करता है, तो उसे इस तरह से करना चाहिए जिससे आप बदल सकें कि आप कैसे लॉगिंग कर रहे हैं। ऐसा करने का सबसे सरल तरीका एक इंटरफ़ेस IL है जिसे L. द्वारा कार्यान्वित किया गया है:

  • घटक डी लॉग करने के लिए एक आईएल का उपयोग करता है; L का उदाहरण दिया गया है
  • घटक डी कार्यक्षमता प्रदान करने के लिए I का उपयोग करता है; ए का उदाहरण दिया गया है
  • घटक बी एक I का उपयोग करता है; D का उदाहरण दिया गया है

इस तरह, कुछ भी सीधे कुछ और पर निर्भर नहीं करता है, जिससे उन्हें बाहर स्वैप करना आसान हो जाता है। यह परिवर्तन को अनुकूलित करना आसान बनाता है, और सिस्टम के कुछ हिस्सों का मज़ाक करना आसान है ताकि आप इकाई परीक्षण कर सकें।


मैं वास्तव में केवल C # को जानता हूं जिसमें मूल प्रतिनिधिमंडल का समर्थन है। इसलिए मैंने लिखा है D implements I। आपके उत्तर के लिए धन्यवाद।
ऐच

1

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

उदाहरण:

class Logger {
   ActuallLogger logger;
   public Action ComposeLog(string msg, Action action) {
      return () => {
          logger.debug(msg);
          action();
      };
   }
}

2
Downvoted। लॉगिंग वास्तव में एक क्रॉस-कटिंग चिंता है। तो आपके कोड में अनुक्रमण विधि कॉल है। यह SRP के उल्लंघन का दावा करने के लिए पर्याप्त नहीं है। अपने आवेदन में एक विशिष्ट घटना की घटना को लॉग करना चिंता का विषय नहीं है। जिस तरह से ये संदेश किसी भी इच्छुक उपयोगकर्ता तक पहुंचाए जाते हैं, वह वास्तव में एक अलग चिंता का विषय है और कार्यान्वयन कोड में इसका वर्णन करना एसआरपी का उल्लंघन है।
लॉरेंट ला रिज़्ज़ा

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

यह कार्यान्वयन विवरण नहीं है। आपके कोड के आकार पर इसका गहरा प्रभाव पड़ता है।
लॉरेंट ला रिज़्ज़ा

मुझे लगता है कि मैं SRP को "WHAT do this function do" के परिप्रेक्ष्य से देख रहा हूँ जहाँ आप SRP को "HOW does this function do it" के परिप्रेक्ष्य से देख रहे हैं।
पॉल निकोविक्ज़
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.