बेस क्लास के समान फ़ाइल में इंटरफ़ेस की घोषणा करना, क्या यह एक अच्छा अभ्यास है?


29

विनिमेय और परीक्षण योग्य होने के लिए, आमतौर पर तर्क के साथ सेवाओं के लिए इंटरफ़ेस की आवश्यकता होती है, जैसे

public class FooService: IFooService 
{ ... }

डिजाइन-वार, मैं इससे सहमत हूं, लेकिन एक चीज जो मुझे इस दृष्टिकोण से परेशान करती है वह यह है कि एक सेवा के लिए आपको दो चीजों (वर्ग और इंटरफ़ेस) की घोषणा करने की आवश्यकता होगी, और हमारी टीम में, सामान्य रूप से दो फाइलें (एक वर्ग के लिए और इंटरफ़ेस के लिए एक)। एक और असुविधा नेविगेशन में कठिनाई है क्योंकि IDE (VS2010) में "गो टू डेफिनिशन" का उपयोग करके इंटरफ़ेस को इंगित करेगा (चूंकि अन्य वर्ग इंटरफ़ेस को संदर्भित करते हैं), वास्तविक वर्ग को नहीं।

मैं सोच रहा था कि FooService के समान फ़ाइल में IFooService लिखने से उपरोक्त अजीबता कम हो जाएगी। आखिरकार, IFooService और FooService बहुत संबंधित हैं। क्या यह अच्छा अभ्यास है? क्या कोई अच्छा कारण है कि IFooService को अपनी फ़ाइल में स्थित होना चाहिए?


3
यदि आपके पास केवल किसी विशेष इंटरफ़ेस का एक कार्यान्वयन है, तो इंटरफ़ेस के लिए कोई वास्तविक तार्किक आवश्यकता नहीं है। सीधे वर्ग का उपयोग क्यों नहीं करते?
जॉरिस टिम्मरमन्स

11
@MadKeithV ढीली युग्मन और परीक्षण क्षमता के लिए?
लुई राइस

10
@MadKeithV: आप सही हैं। लेकिन जब आप कोड है कि IFooService पर निर्भर करता है के लिए इकाई परीक्षण लिखते हैं, आप आम तौर पर एक MockFooService, जो प्रदान करेगा है कि इंटरफेस की एक दूसरी कार्यान्वयन।
डॉक्टर ब्राउन

5
@DocBrown - यदि आपके पास कई कार्यान्वयन हैं, तो जाहिर है कि टिप्पणी दूर हो जाती है, लेकिन इसलिए सीधे "एकल कार्यान्वयन" पर जाने में सक्षम होने की उपयोगिता है (क्योंकि कम से कम दो हैं)। फिर सवाल यह हो जाता है: कंक्रीट कार्यान्वयन में कौन सी जानकारी पहले से ही इंटरफ़ेस विवरण / प्रलेखन में नहीं होनी चाहिए, जहां आप इंटरफ़ेस का उपयोग कर रहे हैं?
जॉरिस टिम्मरमन्स

1
स्वान्तः सुखाय विज्ञापन: stackoverflow.com/questions/5840219/…
मार्जन वेनेमा

जवाबों:


24

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

इसके अलावा, आप सही कह रहे हैं कि "गो टू डेफिनिशन" आपको इंटरफ़ेस पर ले जाता है, लेकिन अगर आपके पास रेस्परर स्थापित है, तो यह उस इंटरफ़ेस से व्युत्पन्न वर्गों / इंटरफेस की एक सूची खोलने के लिए केवल एक क्लिक है, इसलिए यह कोई बड़ी बात नहीं है। इसलिए मैं इंटरफ़ेस को एक अलग फ़ाइल में रखता हूं।

 


5
मैं इस जवाब पर सहमत हूं, आखिरकार आईडीई को हमारे डिजाइन को समायोजित करना चाहिए और अन्य तरीके से नहीं, ठीक है?
निशान्त

1
इसके अलावा, Resharper के बिना, F12 और Shift + F12 बहुत कुछ एक ही काम करते हैं। एक सही क्लिक भी दूर नहीं है :) (निष्पक्ष होने के लिए, यह आपको केवल इंटरफ़ेस के प्रत्यक्ष उपयोग के लिए ले जाएगा, यह निश्चित नहीं है कि Resharper संस्करण क्या करता है)
डैनियल बी

@ डैनियलबी: रेसपेर में, ऑल्ट-एंड कार्यान्वयन पर जाता है (या आपको जाने के लिए संभावित कार्यान्वयन की सूची प्रस्तुत करता है)।
स्ट्रिपिंगवर्यर

@StriplingWarrior तो ऐसा लगता है कि यह वास्तव में वैनिला VS क्या करता है। F12 = परिभाषा पर जाएं, Shift + F12 = क्रियान्वयन पर जाएं (मुझे पूरा यकीन है कि यह Resharper के बिना काम करता है, हालांकि मेरे पास यह स्थापित है, इसलिए इसकी पुष्टि करना मुश्किल है)। यह सबसे उपयोगी नेविगेशन शॉर्टकट में से एक है, मैं सिर्फ शब्द फैला रहा हूं।
डैनियल बी

@ डैनियलबी: शिफ्ट-एफ 12 के लिए डिफॉल्ट "यूजेज ढूंढें।" jetbrains.com/resharper/webhelp/…
स्ट्रिपिंगवर्कर

15

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


10

एसओएलआईडी के अनुसार, न केवल आपको इंटरफ़ेस बनाना चाहिए, और न केवल यह एक अलग फाइल में होना चाहिए, यह एक अलग विधानसभा में होना चाहिए।

क्यूं कर? क्योंकि किसी स्रोत फ़ाइल का असेंबली में संकलित करने के लिए असेंबली के पुन: संकलन की आवश्यकता होती है, और असेंबली में किसी भी परिवर्तन के लिए किसी भी निर्भर असेंबली के पुनर्संयोजन की आवश्यकता होती है। इसलिए, यदि आपका उद्देश्य, SOLID पर आधारित है, तो कार्यान्वयन A को कार्यान्वयन B से बदलने में सक्षम होना है, जबकि वर्ग C इंटरफ़ेस पर निर्भर है, मुझे अंतर नहीं जानना है, आपको यह सुनिश्चित करना होगा कि I के साथ असेंबली हो इसमें परिवर्तन नहीं होता है, इस प्रकार यह रक्षा करता है।

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

अब, अस्वीकरण: यह हमेशा संभव या संभव नहीं है। ऐसा करने का सबसे आसान तरीका एक केंद्रीकृत "इंटरफेस" परियोजना बनाना है। इसका अपना डाउनसाइड है; कोड कम पुन: प्रयोज्य हो जाता है क्योंकि इंटरफ़ेस प्रोजेक्ट और कार्यान्वयन परियोजना को अन्य एप्लिकेशन में संदर्भित किया जाता है जो आपके ऐप की दृढ़ता परत या अन्य प्रमुख घटकों का पुन: उपयोग कर रहा है। आप अधिक कसकर युग्मित विधानसभाओं में इंटरफेस को विभाजित करके उस समस्या को दूर कर सकते हैं, लेकिन फिर आपके ऐप में अधिक परियोजनाएं हैं जो एक पूर्ण निर्माण को बहुत दर्दनाक बनाती हैं। कुंजी संतुलन है, और शिथिल-युग्मित डिजाइन को बनाए रखना है; आप आमतौर पर आवश्यक के रूप में फ़ाइलों को स्थानांतरित कर सकते हैं, इसलिए जब आप देखते हैं कि एक वर्ग को कई परिवर्तनों की आवश्यकता होगी, या कि इंटरफ़ेस के नए कार्यान्वयन की नियमित रूप से आवश्यकता होगी (शायद अन्य सॉफ़्टवेयर के नए-समर्थित संस्करणों के साथ इंटरफ़ेस करने के लिए,


8

अलग-अलग फ़ाइलों को असुविधा क्यों होती है? मेरे लिए, यह बहुत अधिक नट और क्लीनर है। "इंटरफेसेस" नाम का सबफ़ोल्डर बनाना और आपकी IFooServer.cs फ़ाइलों को वहीं रखना आम है, यदि आप अपने सोल्यूशन एक्सप्लोरर में कम फाइल्स देखना चाहते हैं।

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


6

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

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

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


3
+1 इंगित करने के लिए कि टीम के अभ्यासों को फ़ाइल संरचना को निर्धारित करना चाहिए और मानक दृष्टिकोण के विपरीत होना चाहिए।

3

कई बार ऐसा होता है जहाँ आप चाहते हैं कि आपका इंटरफ़ेस न केवल क्लास के लिए एक अलग फ़ाइल में हो, बल्कि पूरी तरह से एक अलग असेंबली में भी हो।

उदाहरण के लिए, यदि आप तार के दोनों सिरों पर नियंत्रण रखते हैं, तो WCF सेवा अनुबंध इंटरफ़ेस क्लाइंट और सेवा दोनों द्वारा साझा किया जा सकता है । इंटरफ़ेस को अपनी असेंबली में ले जाने से, इसकी असेंबली निर्भरता कम होगी। इससे ग्राहक को उपभोग करने के लिए बहुत आसान हो जाता है, इसके कार्यान्वयन के साथ युग्मन को ढीला करना।


2

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

जब आप इंटरफ़ेस के साथ सह-पता लगाने वाले वर्ग को अमूर्त करते हैं , और आप जानते हैं कि आपके इंटरफ़ेस के सभी कार्यान्वयनों को उस सार वर्ग को प्राप्त करना चाहिए, तो एक ही फ़ाइल में दो का पता लगाना समझ में आता है। आपको अभी भी एक इंटरफ़ेस का उपयोग करने के अपने निर्णय की जांच करनी चाहिए: अपने आप से पूछें कि क्या एक अमूर्त वर्ग अपने आप ठीक होगा, और यदि जवाब सकारात्मक है तो इंटरफ़ेस को छोड़ दें।

आम तौर पर, हालांकि, आपको "एक सार्वजनिक वर्ग / इंटरफ़ेस एक फ़ाइल से मेल खाती है" रणनीति से चिपके रहना चाहिए: इसका पालन करना आसान है, और यह आपके स्रोत के पेड़ को नेविगेट करने में आसान बनाता है।


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


3
यह वास्तव में .NET में एक वर्ग के लिए एक इंटरफ़ेस के लिए एक बहुत ही सामान्य पैटर्न है, क्योंकि यह यूनिट परीक्षणों को मॉक, स्टब्स, स्पीस, या अन्य परीक्षण डबल्स के साथ निर्भरता की अनुमति देता है।
पीट

2
@Pete मैंने इसे नोट के रूप में शामिल करने के लिए अपना उत्तर संपादित किया। मैं इस पैटर्न को "सामान्य" नहीं कहूंगा, क्योंकि यह आपके परीक्षण की चिंता को आपके मुख्य कोड आधार में "लीक" करने देता है। आधुनिक मॉकिंग फ्रेमवर्क आपको इस समस्या को काफी हद तक दूर करने में मदद करते हैं, लेकिन वहां एक इंटरफ़ेस होने से निश्चित रूप से चीजों को बहुत सरल बनाया जाता है।
dasblinkenlight

2
@KeithS एक इंटरफ़ेस के बिना एक वर्ग केवल आपके डिज़ाइन में होना चाहिए जब आप मृत-निश्चित हों कि इसका उपवर्ग बनाने का कोई मतलब नहीं है, और आप उस वर्ग को बनाते हैं sealed। यदि आपको संदेह है कि आप भविष्य में किसी समय एक दूसरे उपवर्ग को जोड़ सकते हैं, तो आप तुरंत इंटरफ़ेस में डाल सकते हैं। PS एक सभ्य-गुणवत्ता वाली रीफैक्टरिंग असिस्टेंट आपके लिए एक ही ReSharper लाइसेंस की मामूली कीमत पर हो सकती है :)
dasblinkenlight

1
@KeithS और हाँ, आपके सभी वर्ग जो विशेष रूप से उपवर्ग के लिए डिज़ाइन नहीं किए गए हैं, उन्हें सील कर दिया जाना चाहिए। वास्तव में, इच्छा वर्गों को डिफ़ॉल्ट रूप से सील कर दिया गया था, आपको स्पष्ट रूप से विरासत के लिए कक्षाएं नामित करने के लिए मजबूर किया गया था, उसी तरह जिस तरह से भाषा वर्तमान में आपको चिह्नित करके ओवरराइडिंग के लिए फ़ंक्शंस को नामित करने के लिए मजबूर करती है virtual
dasblinkenlight 15

2
@KeithS: जब आपका एक कार्यान्वयन दो हो जाए तो क्या होगा? जब आपका कार्यान्वयन बदलता है, तब भी वही होता है; आप उस परिवर्तन को दर्शाने के लिए कोड बदलते हैं। YAGNI यहां लागू होता है। आप कभी भी यह नहीं जान पाएंगे कि भविष्य में क्या बदलने वाला है, इसलिए सबसे सरल काम संभव है। (दुर्भाग्य से परीक्षण इस मैक्सिम के साथ
खिलवाड़

2

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

क्लाइंट कोड इंटरफ़ेस पर निर्भर करता है। यह क्लाइंट कोड और कार्यान्वयन के बिना इंटरफ़ेस को संकलित और तैनात करने की संभावना देता है । आपके पास क्लाइंट कोड की तरह प्लगइन्स की तरह काम करने वाले विभिन्न कार्यान्वयन हो सकते हैं ।

अद्यतन: यह एक चुस्त-एकमात्र सिद्धांत नहीं है। गैंग ऑफ़ फोर इन डिज़ाइन पैटर्न, '94 में वापस, पहले से ही इंटरफेस का पालन करने वाले और इंटरफेस के लिए प्रोग्रामिंग का पालन करने वाले ग्राहकों के बारे में बात कर रहे हैं। उनका नजरिया भी ऐसा ही है।


1
Agile के पास उस डोमेन से परे इंटरफेस का उपयोग अच्छी तरह से हो जाता है। एजाइल एक विकास पद्धति है जो विशेष रूप से भाषा निर्माण का उपयोग करती है। एक इंटरफ़ेस एक भाषा निर्माण है।

1
हां, लेकिन इंटरफ़ेस अभी भी क्लाइंट का है और कार्यान्वयन का नहीं, इस बात की परवाह किए बिना कि हम चुस्त हैं या नहीं।
Patkos Csaba

मैं इस पर आपके साथ हूं।
मार्जन वेनेमा

0

मैं व्यक्तिगत रूप से एक इंटरफ़ेस के सामने "I" पसंद नहीं करता। इस प्रकार के उपयोगकर्ता के रूप में मैं यह नहीं देखना चाहता कि यह एक इंटरफ़ेस है या नहीं। प्रकार दिलचस्प बात है। निर्भरता के संदर्भ में आपका FooService IFooService का एक संभावित कार्यान्वयन है। इंटरफ़ेस उस जगह के पास होना चाहिए जहां इसका उपयोग किया जाता है। दूसरी ओर कार्यान्वयन एक ऐसी जगह पर होना चाहिए जहां क्लाइंट को प्रभावित किए बिना इसे आसानी से बदला जा सके। इसलिए, मैं सामान्य रूप से दो फाइलें पसंद करूंगा।


2
एक उपयोगकर्ता के रूप में, मैं यह जानना चाहता हूं कि क्या कोई प्रकार एक इंटरफ़ेस है, क्योंकि उदाहरण के लिए इसका मतलब है कि मैं newइसके साथ उपयोग नहीं कर सकता , लेकिन दूसरी ओर, मैं इसका सह-या गर्भनिरोधक रूप से उपयोग कर सकता हूं। और I.Net में एक स्थापित नामकरण सम्मेलन है, इसलिए इसका पालन करना एक अच्छा कारण है, यदि केवल अन्य प्रकारों के साथ संगतता के लिए।
svick

के साथ शुरू Iमें इंटरफ़ेस केवल दो फाइलों में जुदाई के बारे में मेरे तर्क का परिचय था। कोड बेहतर पठनीय है और यह निर्भरता के व्युत्क्रम को अधिक स्पष्ट बनाता है।
12

4
यह पसंद है या नहीं, इंटरफ़ेस नाम के सामने 'I' का उपयोग करना .NET में एक वास्तविक तथ्य है। एक .NET परियोजना में मानक का पालन नहीं करना मेरे दृष्टिकोण में 'कम से कम विस्मय के सिद्धांत' का उल्लंघन होगा।
पीट

मेरा मुख्य बिंदु है: दो फाइलों में अलग। अमूर्त के लिए एक और कार्यान्वयन के लिए एक। यदि Iआपके वातावरण में उपयोग सामान्य है: इसका उपयोग करें! (लेकिन मुझे यह पसंद नहीं है :))
ollins

और P.SE के मामले में, I को एक इंटरफ़ेस के सामने उपसर्ग करने से यह और अधिक स्पष्ट हो जाता है कि पोस्टर इंटरफ़ेस के बारे में बात कर रहा है, दूसरे वर्ग से विरासत में नहीं मिला है।

0

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


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

0

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

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

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


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