2008-10-19 9 views
37

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

मान लीजिए मैं एक वीडियो स्टोर आवेदन जिसका डेटाबेस एक Person टेबल है, PersonId, Name, DateOfBirth और Address क्षेत्रों के साथ लिख रहा हूँ। इसमें Staff तालिका भी है, जिसमें PersonId का लिंक है, और Customer तालिका है जो PersonId से भी जुड़ी है।

class Person { 
    public int PersonId { get; set; } 
    public string Name { get; set; } 
    public DateTime DateOfBirth { get; set; } 
    public string Address { get; set; } 
} 

class Customer : Person { 
    public int CustomerId { get; set; } 
    public DateTime JoinedDate { get; set; } 
} 

class Staff : Person { 
    public int StaffId { get; set; } 
    public string JobTitle { get; set; } 
} 

अब हम एक समारोह लिख सकते हैं का कहना है कि सभी ग्राहकों के लिए ईमेल भेजने के लिए:

एक साधारण वस्तु उन्मुख दृष्टिकोण कहना है कि एक CustomerPerson "एक" और इसलिए कक्षाएं इस तरह एक सा बनाने होगा :

static void SendEmailToCustomers(IEnumerable<Person> everyone) { 
    foreach(Person p in everyone) 
     if(p is Customer) 
      SendEmail(p); 
} 

यह प्रणाली तब तक ठीक काम करती है जब तक हमारे पास कोई ग्राहक न हो और कर्मचारी दोनों सदस्य हो। यह मानते हुए कि हम नहीं वास्तव में हमारे everyone सूची एक बार एक Customer के रूप में और एक बार एक Staff के रूप में, दो बार में एक ही व्यक्ति के लिए चाहते हैं, हम के बीच स्वेच्छाचारी पसंद करते हैं:

class StaffCustomer : Customer { ... 

और

class StaffCustomer : Staff { ... 

स्पष्ट रूप से केवल इन दोनों में से पहला SendEmailToCustomers फ़ंक्शन नहीं तोड़ देगा।

तो आप क्या करेंगे?

  • Person वर्ग बनाओ एक StaffDetails और CustomerDetails वर्ग के लिए वैकल्पिक संदर्भ है?
  • एक नई कक्षा बनाएं जिसमें Person, प्लस वैकल्पिक StaffDetails और CustomerDetails शामिल है?
  • सबकुछ एक इंटरफ़ेस बनाएं (उदा। IPerson, IStaff, ICustomer) और उचित इंटरफेस लागू करने वाले तीन वर्ग बनाएं?
  • एक और पूरी तरह से अलग दृष्टिकोण ले लो?

उत्तर

47

मार्क, यह एक दिलचस्प सवाल है। आपको इस पर कई राय मिलेंगी। मुझे विश्वास नहीं है कि एक 'सही' जवाब है। यह एक महान उदाहरण है जहां एक कठोर हेरार्कियल ऑब्जेक्ट डिज़ाइन वास्तव में सिस्टम के निर्माण के बाद समस्याएं पैदा कर सकता है।

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

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

यहां आपके उदाहरण के लिए, मैं "एक और पूरी तरह से अलग दृष्टिकोण ले" के साथ जाऊंगा। मैं व्यक्ति वर्ग को लागू करता हूं और इसमें "भूमिकाएं" का संग्रह शामिल करता हूं। प्रत्येक व्यक्ति के पास "ग्राहक", "कर्मचारी" और "विक्रेता" जैसी एक या अधिक भूमिकाएं हो सकती हैं।

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

10

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

+0

हां, और अब आप एक कार्यान्वयन चुन सकते हैं और अन्य कोड तोड़ने के बाद बाद में अपना मन बदल सकते हैं। –

16

आप Party and Accountability patterns

इस तरह व्यक्ति का उपयोग कर किस प्रकार ग्राहक या स्टाफ की हो सकती है Accountabilities का एक संग्रह होगा विचार कर सकते हैं।

यदि आप बाद में अधिक रिश्ते के प्रकार जोड़ते हैं तो मॉडल भी आसान होगा।

1

हम पिछले साल कॉलेज में इस समस्या का अध्ययन करते हैं, हम एफिल सीख रहे थे इसलिए हमने कई विरासत का उपयोग किया। वैसे भी Foredecker भूमिका विकल्प पर्याप्त लचीला प्रतीत होता है।

3

मैं "है" चेक ("जावा में उदाहरण") से बचूंगा। एक समाधान Decorator Pattern का उपयोग करना है। आप एक EmailablePerson बना सकते हैं जो व्यक्ति को सजाने वाला है जहां EmailablePerson किसी व्यक्ति का निजी उदाहरण रखने के लिए संरचना का उपयोग करता है और व्यक्ति ऑब्जेक्ट को सभी गैर-ईमेल विधियों को प्रतिनिधि करता है।

1

एक कर्मचारी को ईमेल भेजने में क्या गलत है जो कर्मचारी सदस्य है? यदि वह एक ग्राहक है, तो उसे ईमेल भेजा जा सकता है। क्या मैं ऐसा सोचने में गलत हूं? और आपको अपनी ईमेल सूची के रूप में "हर कोई" क्यों लेना चाहिए? Woudlnt ग्राहक की सूची रखना बेहतर होगा क्योंकि हम "sendEmailToCustomer" विधि से निपट रहे हैं और "sendEmailToEveryone" विधि नहीं? भले ही आप "हर कोई" सूची का उपयोग करना चाहते हैं, आप उस सूची में डुप्लिकेट की अनुमति नहीं दे सकते।

यदि इनमें से कोई भी बहुत से redisgn के साथ प्राप्त करने योग्य नहीं है, तो मैं पहले फोर्डकेकर उत्तर के साथ जाऊंगा और हो सकता है कि आपको प्रत्येक व्यक्ति को कुछ भूमिकाएं दी जानी चाहिए।

+0

दिए गए उदाहरण में, कोई व्यक्ति ग्राहक और कर्मचारी दोनों सदस्य नहीं हो सकता है। सवाल यही है। – OregonGhost

+0

हाय, मुझे लगता है कि प्रश्न के बारे में "मैं कई ईमेल भेजने के लिए अगर एक व्यक्ति दोनों एक ग्राहक और एक स्टाफ सदस्य है नहीं करना चाहती" अधिक है। इस समस्या को हल करने के लिए, 1) "सभी" अनुमति नहीं देनी चाहिए डुप्लिकेट 2) यह डुप्लिकेट तो व्यक्ति वर्ग "भूमिकाओं" के रूप में Foredecker – vj01

5

मुझे बताएं कि क्या मैं फोर्डकेकर के जवाब को सही ढंग से समझता हूं। यहां मेरा कोड है (पायथन में; क्षमा करें, मुझे सी # नहीं पता)। एकमात्र अंतर यह है कि यदि कोई व्यक्ति "ग्राहक है" तो मैं कुछ सूचित नहीं करूंगा, अगर मैं उसकी भूमिका में से किसी एक में दिलचस्पी लेता हूं तो मैं ऐसा करूँगा। क्या यह लचीला है?

# --------- PERSON ---------------- 

class Person: 
    def __init__(self, personId, name, dateOfBirth, address): 
     self.personId = personId 
     self.name = name 
     self.dateOfBirth = dateOfBirth 
     self.address = address 
     self.roles = [] 

    def addRole(self, role): 
     self.roles.append(role) 

    def interestedIn(self, subject): 
     for role in self.roles: 
      if role.interestedIn(subject): 
       return True 
     return False 

    def sendEmail(self, email): 
     # send the email 
     print "Sent email to", self.name 

# --------- ROLE ---------------- 

NEW_DVDS = 1 
NEW_SCHEDULE = 2 

class Role: 
    def __init__(self): 
     self.interests = [] 

    def interestedIn(self, subject): 
     return subject in self.interests 

class CustomerRole(Role): 
    def __init__(self, customerId, joinedDate): 
     self.customerId = customerId 
     self.joinedDate = joinedDate 
     self.interests.append(NEW_DVDS) 

class StaffRole(Role): 
    def __init__(self, staffId, jobTitle): 
     self.staffId = staffId 
     self.jobTitle = jobTitle 
     self.interests.append(NEW_SCHEDULE) 

# --------- NOTIFY STUFF ---------------- 

def notifyNewDVDs(emailWithTitles): 
    for person in persons: 
     if person.interestedIn(NEW_DVDS): 
      person.sendEmail(emailWithTitles) 

+0

से कहा हाँ, यह एक अच्छा समाधान लग रहा है और बहुत विस्तृत है परिभाषित होना चाहिए की अनुमति देता है। –

1

आपका कक्षाएं सिर्फ डाटा संरचनाओं हैं: उनमें से कोई भी किसी भी व्यवहार, बस getters और setters है। विरासत यहां अनुचित है।

7

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

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

+0

एक संगठन अक्सर एक प्रकार के व्यक्ति के रूप में योग्यता प्राप्त करता है, यानी एक न्यायपालिका व्यक्ति। – ProfK

1

एक और पूरी तरह से अलग दृष्टिकोण ले लो कक्षा। संभवतः 'isCustomer' के स्टाफ क्लास के भीतर एक साधारण बुलियन कर्मचारी की सदस्य नहीं पाने के लिए हमारी सभी सूची (संभावित रूप से सभी ग्राहकों और सभी कर्मचारियों को उचित टेबल से प्राप्त करने से संकलित) की अनुमति देगा क्योंकि यह पता चलेगा कि इसे पहले ही ग्राहक के रूप में शामिल किया गया है।

1

यहां कुछ अन्य युक्तियां है: "यहां तक ​​कि ऐसा करने के लिए नहीं लगता कि इस" की श्रेणी से यहाँ कोड के कुछ बुरा उदाहरण का सामना करना पड़ा हैं:

खोजक विधि रिटर्न वस्तु

समस्या: संख्या के आधार पर घटनाओं में पाया गया कि खोजक विधि घटनाओं की संख्या का प्रतिनिधित्व करने वाली संख्या लौटाती है - या! अगर केवल एक पाया वास्तविक वस्तु देता है।

ऐसा मत करो! यह सबसे खराब कोडिंग प्रथाओं में से एक है और यह अस्पष्टता प्रस्तुत करता है और कोड को इस तरह से गड़बड़ करता है कि जब एक अलग डेवलपर खेल में आता है तो वह ऐसा करने के लिए आपको नफरत करेगा।

समाधान: यदि ऐसी 2 कार्यक्षमताओं की आवश्यकता है: किसी उदाहरण को गिनने और लाने से 2 विधियां होती हैं जो गिनती लौटाती हैं और जो उदाहरण देता है, लेकिन दोनों तरीकों से कोई भी तरीका नहीं करता है।

समस्या: एक व्युत्पन्न खराब अभ्यास तब होता है जब एक खोजकर्ता विधि या तो एक ही घटना को या तो एक से अधिक घटनाओं में पाया जाता है यदि एक से अधिक पाए जाते हैं। यह आलसी प्रोग्रामिंग शैली प्रोग्रामर द्वारा बहुत कुछ किया जाता है जो सामान्य रूप से पिछले एक को करते हैं।

समाधान: यह मेरे हाथों पर होने पर मैं लंबाई 1 (एक) की एक सरणी वापस कर दूंगा यदि केवल एक घटना पाई जाती है और लंबाई के साथ एक सरणी> 1 यदि अधिक घटनाएं मिलती हैं। इसके अलावा, आवेदन के आधार पर कोई भी घटना नहीं ढूंढने से शून्य या लंबाई 0 की सरणी वापस आ जाएगी।

एक अंतरफलक के लिए प्रोग्रामिंग और covariant वापसी प्रकार

समस्या का उपयोग कर: एक अंतरफलक के लिए प्रोग्रामिंग और covariant वापसी प्रकार का उपयोग और बुला कोड में कास्टिंग।

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

1000 से अधिक लाइनों वाले वर्ग एक खतरनाक खतरे हैं 100 से अधिक लाइनों वाले तरीके भी एक खतरनाक खतरे हैं!

समस्या: कुछ डेवलपर्स एक वर्ग/विधि में बहुत अधिक कार्यक्षमता रखते हैं, कार्यक्षमता तोड़ने के लिए बहुत आलसी होने के कारण - इससे कम संयोजन होता है और शायद उच्च युग्मन होता है - ओओपी में एक बहुत ही महत्वपूर्ण सिद्धांत के विपरीत! समाधान: बहुत अधिक आंतरिक/घोंसले वाले वर्गों का उपयोग करने से बचें - इन वर्गों का उपयोग प्रति आवश्यकता आधार पर ही किया जाना चाहिए, आपको उनका उपयोग करने की आदत नहीं है! उनका उपयोग करने से विरासत सीमित करने जैसी अधिक समस्याएं हो सकती हैं। कोड डुप्लिकेट के लिए लुकआउट! कुछ समानता कार्यान्वयन या शायद किसी अन्य वर्ग में समान या बहुत ही समान कोड मौजूद हो सकता है। यदि यह किसी अन्य वर्ग में है जो सुपरर्ट टाइप नहीं है तो आपने समेकन नियम का भी उल्लंघन किया है। स्थिर तरीकों के लिए देखें - शायद आपको जोड़ने के लिए उपयोगिता वर्ग की आवश्यकता है!
अधिक: http://centraladvisor.com/it/oop-what-are-the-best-practices-in-oop

0

शायद आप इसके लिए विरासत का उपयोग नहीं करना चाहते हैं। इसके बजाय इसे आजमाएं:

class Person { 
    public int PersonId { get; set; } 
    public string Name { get; set; } 
    public DateTime DateOfBirth { get; set; } 
    public string Address { get; set; } 
} 

class Customer{ 
    public Person PersonInfo; 
    public int CustomerId { get; set; } 
    public DateTime JoinedDate { get; set; } 
} 

class Staff { 
    public Person PersonInfo; 
    public int StaffId { get; set; } 
    public string JobTitle { get; set; } 
} 
+2

क्यों? इसका उत्तर अधिक अर्थपूर्ण होगा यदि इसमें अधिक स्पष्टीकरण शामिल था। यह मौजूदा उत्तरों से अलग कैसे है? – Leigh

संबंधित मुद्दे