लक्षण के बजाय अमूर्त वर्गों का उपयोग करने का क्या फायदा है?


371

एक विशेषता (प्रदर्शन के अलावा) के बजाय एक अमूर्त वर्ग का उपयोग करने का क्या फायदा है? ऐसा लगता है जैसे अमूर्त वर्गों को ज्यादातर मामलों में लक्षणों द्वारा प्रतिस्थापित किया जा सकता है।

जवाबों:


371

मैं दो अंतरों के बारे में सोच सकता हूं

  1. एब्सट्रैक्ट क्लासेस में कंस्ट्रक्टर पैरामीटर के साथ-साथ टाइप पैरामीटर भी हो सकते हैं। लक्षणों में केवल प्रकार के पैरामीटर हो सकते हैं। कुछ चर्चा थी कि भविष्य में भी लक्षणों में कंस्ट्रक्टर पैरामीटर हो सकते हैं
  2. सार कक्षाएं जावा के साथ पूरी तरह से परस्पर जुड़ी हुई हैं। आप उन्हें बिना किसी रैपर के जावा कोड से कॉल कर सकते हैं। लक्षण पूरी तरह से इंटरऑपरेबल होते हैं केवल अगर उनमें कोई कार्यान्वयन कोड नहीं होता है

172
बहुत महत्वपूर्ण परिशिष्ट: एक वर्ग कई लक्षणों से विरासत में मिल सकता है लेकिन केवल एक सार वर्ग। मुझे लगता है कि यह पहला सवाल होना चाहिए जो एक डेवलपर पूछता है जब विचार करना है कि लगभग सभी मामलों में उपयोग करना है।
BAR

15
लाइफसेवर: "लक्षण पूरी तरह से इंटरऑपरेबल हैं, केवल अगर उनमें कोई कार्यान्वयन कोड नहीं है"
वालरस द कैट

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

5
Java8 में दूसरा अंतर मौजूद नहीं है, सोचिए।
डुओन्ग गुयेन

14
स्कला 2.12 के अनुसार, एक विशेषता जावा 8 इंटरफ़ेस - scala-lang.org/news/2.12.0#traits-compile-to-interfaces पर संकलित होती है
केविन मेरेडिथ

209

स्कैला में प्रोग्रामिंग में एक सेक्शन है "To trait, or not to trait?" जो इस प्रश्न को संबोधित करता है। चूँकि 1 एड ऑनलाइन उपलब्ध है, मुझे उम्मीद है कि यहाँ पूरी बात उद्धृत करना ठीक है। (किसी भी गंभीर स्काला प्रोग्रामर को पुस्तक खरीदनी चाहिए):

जब भी आप व्यवहार के एक पुन: प्रयोज्य संग्रह को लागू करते हैं, तो आपको यह तय करना होगा कि आप किसी विशेषता या सार वर्ग का उपयोग करना चाहते हैं। कोई ठोस नियम नहीं है, लेकिन इस खंड में विचार करने के लिए कुछ दिशानिर्देश हैं।

यदि व्यवहार का पुन: उपयोग नहीं किया जाएगा , तो इसे एक ठोस वर्ग बनाएं। यह सब के बाद पुन: प्रयोज्य व्यवहार नहीं है।

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

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

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

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

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

जैसा कि @ मुश्ताक अहमद ने उल्लेख किया है, किसी गुण को किसी वर्ग के प्राथमिक निर्माता को पारित नहीं किया जा सकता है।

एक और अंतर इसका इलाज है super

वर्गों और लक्षणों के बीच दूसरा अंतर यह है कि कक्षाओं में, superकॉल सांख्यिकीय रूप से बाध्य होते हैं, लक्षणों में, वे गतिशील रूप से बाध्य होते हैं। यदि आप super.toStringएक कक्षा में लिखते हैं , तो आप ठीक से जानते हैं कि किस विधि को लागू किया जाएगा। जब आप एक विशेषता में एक ही बात लिखते हैं, हालांकि, जब आप विशेषता को परिभाषित करते हैं तो सुपर कॉल के लिए आह्वान करने के लिए विधि कार्यान्वयन अपरिभाषित होता है।

अधिक जानकारी के लिए अध्याय 12 के बाकी हिस्सों को देखें।

1 संपादित करें (2013):

लक्षण की तुलना में अमूर्त वर्गों के व्यवहार के तरीके में एक सूक्ष्म अंतर है। रैखिककरण नियमों में से एक यह है कि यह वर्गों की वंशानुगत पदानुक्रम को संरक्षित करता है, जो बाद में श्रृंखला में अमूर्त वर्गों को आगे बढ़ाता है जबकि लक्षण खुशी से मिश्रित हो सकते हैं। कुछ परिस्थितियों में, यह वास्तव में वर्ग रेखीयकरण के बाद की स्थिति में होना बेहतर होता है। , इसलिए अमूर्त वर्गों को इसके लिए इस्तेमाल किया जा सकता है। स्काला में वर्ग रेखीयकरण (मिक्सिन ऑर्डर) के लिए विवश देखें ।

2 संपादित करें (2018):

स्कला 2.12 के अनुसार, ट्रिट की द्विआधारी संगतता व्यवहार बदल गया है। २.१२ से पहले, सभी वर्गों में आवश्यक परिवर्तन के लिए सदस्य को जोड़ने या हटाने के लिए, जो वर्ग को परिवर्तित नहीं करता है, भले ही गुण को विरासत में मिला हो। इसका कारण है कि जिस तरह से जेवीएम में लक्षण अंकित किए गए थे।

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

  • परिभाषित फ़ील्ड ( valया var, लेकिन एक निरंतर ठीक है - final valपरिणाम प्रकार के बिना)
  • बुला super
  • शरीर में इनिशाइज़र स्टेटमेंट
  • एक वर्ग का विस्तार
  • सही सुपरसट्रेट में कार्यान्वयन खोजने के लिए रैखिककरण पर निर्भर

लेकिन अगर विशेषता नहीं है, तो आप अब इसे बाइनरी संगतता को तोड़ने के बिना अपडेट कर सकते हैं।


2
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine- क्या कोई समझा सकता है कि यहाँ क्या अंतर है? extendsबनाम with?
0fnt

2
@ 0fnt उसका अंतर बनाम के साथ विस्तार नहीं है। वह जो कह रहा है वह यह है कि यदि आप केवल उसी संकलन के भीतर विशेषता में मिश्रण करते हैं, तो द्विआधारी संगतता मुद्दे लागू नहीं होते हैं। हालांकि, यदि आपका एपीआई उपयोगकर्ताओं को स्वयं विशेषता में मिश्रण करने की अनुमति देने के लिए डिज़ाइन किया गया है, तो आपको द्विआधारी संगतता के बारे में चिंता करनी होगी।
जॉन कोलंडोनी

2
@ 0fnt: extendsऔर के बीच कोई शब्दार्थ अंतर नहीं है with। यह विशुद्ध रूप से वाक्यात्मक है। यदि आप कई टेम्प्लेट से विरासत में प्राप्त करते हैं, तो पहला मिलता है extend, अन्य सभी को मिलता है with, बस। withएक अल्पविराम के रूप में सोचो class Foo extends Bar, Baz, Qux:।
जोर्ज डब्ल्यू मित्तग

77

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


20

इस तथ्य के अलावा कि आप सीधे कई अमूर्त वर्गों का विस्तार नहीं कर सकते हैं, लेकिन आप एक वर्ग में कई लक्षणों को मिला सकते हैं, यह ध्यान देने योग्य है कि लक्षण स्टैकेबल हैं, क्योंकि किसी विशेषता में सुपर कॉल गतिशील रूप से बाध्य हैं (यह एक वर्ग या विशेषता का उल्लेख कर रहा है) वर्तमान वाला)।

सार वर्ग और विशेषता के बीच अंतर में थॉमस के जवाब से :

trait A{
    def a = 1
}

trait X extends A{
    override def a = {
        println("X")
        super.a
    }
}  


trait Y extends A{
    override def a = {
        println("Y")
        super.a
    }
}

scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1@6e9b6a
scala> xy.a
Y
X
res0: Int = 1

scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1

9

अमूर्त वर्ग का विस्तार करते समय, यह दर्शाता है कि उपवर्ग एक समान प्रकार का है। मुझे लगता है कि यह लक्षण का उपयोग करते समय ऐसा नहीं है।


क्या इसका कोई व्यावहारिक प्रभाव है, या क्या यह केवल कोड को समझना आसान बनाता है?
राल्फ


5

एब्सट्रैक्ट क्लासेस में व्यवहार हो सकता है - वे कंस्ट्रक्टर आर्ग (जो लक्षण नहीं कर सकते हैं) के साथ मानकीकृत हो सकते हैं और एक कार्यशील इकाई का प्रतिनिधित्व कर सकते हैं। इसके बजाय लक्षण केवल एक विशेषता, एक कार्यक्षमता का एक इंटरफ़ेस का प्रतिनिधित्व करते हैं।


8
आशा है कि आप यह नहीं समझ रहे हैं कि लक्षणों में व्यवहार नहीं हो सकता। दोनों में कार्यान्वयन कोड हो सकता है।
मिच Blevins 19

1
@ मिच Blevins: बिल्कुल नहीं। उनमें कोड हो सकता है, लेकिन जब आप trait Enumerableबहुत सारे सहायक कार्यों के साथ परिभाषित करते हैं, तो मैं उन्हें व्यवहार नहीं कहूंगा, लेकिन केवल एक सुविधा के साथ जुड़ा कार्यक्षमता।
दारियो

4
@Dario मैं "व्यवहार" और "कार्यक्षमता" को समानार्थक शब्द के रूप में देखता हूं, इसलिए मुझे आपका उत्तर बहुत भ्रमित करने वाला लगता है।
डेविड जे।

3
  1. एक वर्ग कई लक्षणों से विरासत में मिल सकता है लेकिन केवल एक सार वर्ग।
  2. एब्सट्रैक्ट क्लासेस में कंस्ट्रक्टर पैरामीटर के साथ-साथ टाइप पैरामीटर भी हो सकते हैं। लक्षणों में केवल प्रकार के पैरामीटर हो सकते हैं। उदाहरण के लिए, आप विशेषता t (i: Int) {} नहीं कह सकते; i पैरामीटर अवैध है।
  3. सार कक्षाएं जावा के साथ पूरी तरह से परस्पर जुड़ी हुई हैं। आप उन्हें बिना किसी रैपर के जावा कोड से कॉल कर सकते हैं। लक्षण पूरी तरह से इंटरऑपरेबल होते हैं केवल अगर उनमें कोई कार्यान्वयन कोड नहीं होता है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.