स्काला केस कक्षाएं घोषित करने के क्या नुकसान हैं?


105

यदि आप ऐसा कोड लिख रहे हैं जो बहुत सारे सुंदर, अपरिवर्तनीय डेटा संरचनाओं का उपयोग कर रहा है, तो केस क्लासेज एक गॉडसेंड के रूप में दिखाई देती हैं, जो आपको निम्नलिखित में से सभी को केवल एक कीवर्ड के साथ मुफ्त में दे रही हैं:

  • सब कुछ डिफ़ॉल्ट रूप से अपरिवर्तनीय
  • गेट स्वचालित रूप से परिभाषित होते हैं
  • निर्णय लेने () कार्यान्वयन के लिए
  • आज्ञाकारी बराबर () और हैशकोड ()
  • मिलान के लिए अनुपयोगी () विधि के साथ साथी वस्तु

लेकिन एक मामले के रूप में एक अपरिवर्तनीय डेटा संरचना को परिभाषित करने के नुकसान क्या हैं?

यह वर्ग या उसके ग्राहकों पर क्या प्रतिबंध है?

क्या ऐसी परिस्थितियाँ हैं जहाँ आपको गैर-मामले वर्ग को प्राथमिकता देनी चाहिए?


इस संबंधित प्रश्न देखें: stackoverflow.com/q/4635765/156410
डेविड

18
यह रचनात्मक क्यों नहीं है? इस साइट पर मोड बहुत सख्त हैं। इसमें संभावित तथ्यात्मक उत्तरों की एक सीमित संख्या है।
एल्फ

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

जवाबों:


51

एक बड़ा नुकसान: एक केस क्लासेस एक केस क्लास का विस्तार नहीं कर सकता है। वह प्रतिबंध है।

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

मैं उत्परिवर्तित राज्य, निजी राज्य या कोई राज्य (उदाहरण के लिए अधिकांश सिंगलटन घटक) वाली वस्तुओं के लिए गैर-मामले वाले वर्ग को पसंद करता हूं। बहुत अधिक सब कुछ के लिए केस कक्षाएं।


48
आप किसी केस क्लास को हटा सकते हैं। उपवर्ग एक केस क्लास भी नहीं हो सकता है - यह प्रतिबंध है।
सेठ टिस्सू

99

पहले अच्छे बिट्स:

सब कुछ डिफ़ॉल्ट रूप से अपरिवर्तनीय

हां, और varजरूरत पड़ने पर ओवरराइड (उपयोग ) भी किया जा सकता है

गेट स्वचालित रूप से परिभाषित होते हैं

किसी भी वर्ग में संभव है, जिसमें परमान का उपसर्ग हो val

निर्णय toString()लागू करना

हां, बहुत उपयोगी, लेकिन यदि आवश्यक हो तो किसी भी वर्ग पर हाथ से जाने योग्य

आज्ञाकारी equals()औरhashCode()

आसान पैटर्न-मिलान के साथ संयुक्त, यह मुख्य कारण है कि लोग केस कक्षाओं का उपयोग करते हैं

unapply()मिलान के लिए विधि के साथ साथी वस्तु

चिमटा का उपयोग करके किसी भी वर्ग पर हाथ से करना संभव है

इस सूची में uber-शक्तिशाली प्रतिलिपि विधि भी शामिल होनी चाहिए, जो कि स्केल 2.8 में आने वाली सबसे अच्छी चीजों में से एक है


फिर खराब, केस कक्षाओं के साथ केवल कुछ वास्तविक प्रतिबंध हैं:

आप applyकंपाइलर-जनरेटेड मेथड के समान सिग्नेचर का उपयोग करके साथी ऑब्जेक्ट में परिभाषित नहीं कर सकते हैं

हालांकि व्यवहार में, यह शायद ही कभी एक समस्या है। उत्पन्न लागू विधि के व्यवहार को बदलने से उपयोगकर्ताओं को आश्चर्यचकित होने की गारंटी है और इसे दृढ़ता से हतोत्साहित किया जाना चाहिए, ऐसा करने का एकमात्र औचित्य इनपुट मापदंडों को मान्य करना है - मुख्य निर्माणकर्ता निकाय में किया गया एक कार्य जो (उपयोग करते समय भी सत्यापन उपलब्ध कराता हैcopy )

आप उपवर्ग नहीं कर सकते

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

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

उत्पाद के उपवर्ग के रूप में, मामले की कक्षाओं में 22 से अधिक पैरामीटर नहीं हो सकते हैं

कोई वास्तविक वर्कअराउंड नहीं, सिवाय इसके कि कई परेसनों के साथ गाली देना बंद करें :)

इसके अलावा ...

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

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


1
व्यापक उत्तर के लिए धन्यवाद। मुझे लगता है कि सब कुछ अपवाद "आप उपवर्ग नहीं कर सकते हैं" शायद मुझे जल्द ही कभी भी चरणबद्ध करने की संभावना नहीं है।
ग्राहम ली

15
आप किसी केस क्लास को हटा सकते हैं। उपवर्ग एक केस क्लास भी नहीं हो सकता है - यह प्रतिबंध है।
सेठ टिस्सू

5
स्केला 2.11 में केस कक्षाओं के लिए 22-पैरामीटर सीमा हटा दी गई है। मुद्दों .scala
जोनाथन क्रॉसर

यह दावा करना गलत है कि "आप कंपाइलर-जनरेटेड मेथड के समान सिग्नेचर का उपयोग करके साथी ऑब्जेक्ट में अप्लाई नहीं कर सकते हैं"। हालांकि ऐसा करने के लिए कुछ हुप्स के माध्यम से कूदने की आवश्यकता होती है (यदि आप कार्यक्षमता को रखने के लिए चाहते हैं जो स्केला संकलक द्वारा अदृश्य रूप से उत्पन्न किया जाता है), तो यह सबसे निश्चित रूप से प्राप्त किया जा सकता है: stackoverflow.com/a/25538287/525113
chaotic3quilibrium

मैं स्केल मामले की कक्षाओं का बड़े पैमाने पर उपयोग कर रहा हूं और "केस क्लास पैटर्न" (जो अंततः स्काला मैक्रो के रूप में समाप्त हो जाएगा) के साथ आया है जो ऊपर दिए गए कई मुद्दों के साथ मदद करता है: codereview.stackexchange.com/a/98367 / 4758
अराजक 3quilibrium

10

मुझे लगता है कि टीडीडी सिद्धांत यहां लागू होता है: ओवर-डिजाइन नहीं। जब आप कुछ होने की case classघोषणा करते हैं, तो आप बहुत अधिक कार्यक्षमता की घोषणा कर रहे हैं। इससे भविष्य में कक्षा बदलने के लिए आपके पास लचीलापन कम हो जाएगा।

उदाहरण के लिए, कंस्ट्रक्टर मापदंडों पर case classएक equalsविधि है। आप इस बात की परवाह नहीं कर सकते हैं कि जब आप पहली बार अपनी कक्षा लिखते हैं, लेकिन, बाद में, आप तय कर सकते हैं कि आप इनमें से कुछ मापदंडों को अनदेखा करना चाहते हैं, या कुछ अलग करना चाहते हैं। हालाँकि, क्लाइंट कोड को उस समय में लिखा जा सकता है जो case classसमानता पर निर्भर करता है।


4
मुझे नहीं लगता कि ग्राहक कोड को 'बराबर' के सटीक अर्थ पर निर्भर होना चाहिए; यह तय करना एक वर्ग के लिए है कि 'समान' का क्या मतलब है। क्लास लेखक को लाइन के नीचे 'बराबर' के कार्यान्वयन को बदलने के लिए स्वतंत्र होना चाहिए।
pkaeding

8
@pkaeding आप ग्राहक कोड किसी भी निजी पद्धति पर निर्भर नहीं करने के लिए स्वतंत्र हैं। जो कुछ भी सार्वजनिक है वह एक अनुबंध है जिस पर आप सहमत हुए हैं।
डैनियल सी। सोबरल

3
@ डैनियल सी.सोबरल ट्रू, लेकिन अनुबंध में बराबर () (जो फ़ील्ड्स इस पर आधारित है) का सटीक कार्यान्वयन आवश्यक नहीं है। कम से कम, आप स्पष्ट रूप से अनुबंध से बाहर कर सकते हैं जब आप पहली बार कक्षा लिखते हैं।
हरमन

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

2
@ हर्मन मैं क्या कह रहा हूँ में कोई विरोधाभास नहीं है। "उनकी समस्या" के रूप में, यकीन है, जब तक कि यह आपकी समस्या नहीं बन जाती । उदाहरण के लिए, क्योंकि वे आपके स्टार्टअप के बहुत बड़े ग्राहक हैं, या क्योंकि उनके प्रबंधक ऊपरी प्रबंधन को आश्वस्त करते हैं कि उन्हें बदलना बहुत महंगा है, इसलिए आपको अपने परिवर्तनों को पूर्ववत करना होगा, या क्योंकि परिवर्तन एक मिलियन मिलियन डॉलर का कारण बनता है बग और वापस हो जाता है, आदि। लेकिन अगर आप शौक के लिए कोड लिख रहे हैं और उपयोगकर्ताओं की परवाह नहीं करते हैं, तो आगे बढ़ें।
डैनियल सी। सोबरल

7

क्या ऐसी परिस्थितियाँ हैं जहाँ आपको गैर-मामले वर्ग को प्राथमिकता देनी चाहिए?

मार्टिन ओडस्की हमें अपने कोर्स फंकल (लेक्चर 4.6 - पैटर्न मिलान) में कार्यात्मक प्रोग्रामिंग सिद्धांतों का एक अच्छा प्रारंभिक बिंदु देता है जिसका उपयोग हम तब कर सकते हैं जब हमें क्लास और केस क्लास के बीच चयन करना होगा। स्केल बाय उदाहरण के अध्याय 7 में एक ही उदाहरण है।

कहते हैं, हम अंकगणितीय अभिव्यक्तियों के लिए एक दुभाषिया लिखना चाहते हैं। शुरू में चीजों को सरल रखने के लिए, हम अपने आप को सिर्फ संख्याओं और परिचालनों तक सीमित रखते हैं। ऐसे एक्सरे- sions को एक वर्ग पदानुक्रम के रूप में दर्शाया जा सकता है, एक मूल आधार वर्ग के रूप में मूल के रूप में Expr, और दो उपवर्ग संख्या और Sum। फिर, एक अभिव्यक्ति 1 + (3 + 7) के रूप में प्रतिनिधित्व किया जाएगा

नया योग (नया नंबर (1), नया योग (नया नंबर (3), नया नंबर) (7))

abstract class Expr {
  def eval: Int
}

class Number(n: Int) extends Expr {
  def eval: Int = n
}

class Sum(e1: Expr, e2: Expr) extends Expr {
  def eval: Int = e1.eval + e2.eval
}

इसके अलावा, एक नया उत्पाद वर्ग जोड़ने से मौजूदा कोड में कोई परिवर्तन नहीं होता है:

class Prod(e1: Expr, e2: Expr) extends Expr {
  def eval: Int = e1.eval * e2.eval
}

इसके विपरीत, एक नई विधि जोड़ने के लिए सभी मौजूदा वर्गों के संशोधन की आवश्यकता होती है।

abstract class Expr { 
  def eval: Int 
  def print
} 

class Number(n: Int) extends Expr { 
  def eval: Int = n 
  def print { Console.print(n) }
}

class Sum(e1: Expr, e2: Expr) extends Expr { 
  def eval: Int = e1.eval + e2.eval
  def print { 
   Console.print("(")
   print(e1)
   Console.print("+")
   print(e2)
   Console.print(")")
  }
}

केस कक्षाओं के साथ एक ही समस्या हल हुई।

abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
  }
}
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr

एक नई विधि जोड़ना एक स्थानीय परिवर्तन है।

abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
  }
  def print = this match {
    case Number(n) => Console.print(n)
    case Sum(e1,e2) => {
      Console.print("(")
      print(e1)
      Console.print("+")
      print(e2)
      Console.print(")")
    }
  }
}

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

abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
    case Prod(e1,e2) => e1.eval * e2.eval
  }
  def print = this match {
    case Number(n) => Console.print(n)
    case Sum(e1,e2) => {
      Console.print("(")
      print(e1)
      Console.print("+")
      print(e2)
      Console.print(")")
    }
    case Prod(e1,e2) => ...
  }
}

Videolecture से प्रतिलेख 4.6 पैटर्न मिलान

ये दोनों डिज़ाइन पूरी तरह से ठीक हैं और उनके बीच चयन करना कभी-कभी शैली का विषय होता है, लेकिन फिर भी कुछ मापदंड हैं जो महत्वपूर्ण हैं।

एक मानदंड हो सकता है, क्या आप अधिक बार अभिव्यक्ति के नए उप-वर्ग बना रहे हैं या आप अधिक बार नए तरीके बना रहे हैं? तो यह एक ऐसी कसौटी है जो भविष्य की व्यापकता और आपके सिस्टम के संभावित विस्तार पास को देखता है।

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

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

तो इस विलुप्त होने की समस्या दो आयामों में है, जहाँ आप नई कक्षाओं को पदानुक्रम में जोड़ना चाह सकते हैं, या आप नए तरीकों को जोड़ना चाह सकते हैं, या शायद दोनों को, अभिव्यक्ति समस्या का नाम दिया गया है

याद रखें: हमें इसका इस्तेमाल एक शुरुआती बिंदु की तरह करना चाहिए, न कि एकमात्र मापदंड की तरह।

यहां छवि विवरण दर्ज करें


0

मैं Scala cookbookइसे Alvin Alexanderअध्याय 6: से उद्धृत कर रहा हूं objects

यह उन कई चीजों में से एक है जो मुझे इस पुस्तक में दिलचस्प लगीं।

केस क्लास के लिए कई कंस्ट्रक्टर प्रदान करने के लिए, यह जानना महत्वपूर्ण है कि केस क्लास की घोषणा वास्तव में क्या करती है।

case class Person (var name: String)

यदि आप कोड को देखते हैं कि स्केला संकलक केस क्लास उदाहरण के लिए उत्पन्न होता है, तो आप देखेंगे कि यह दो आउटपुट, व्यक्ति $ .class और Person.class बनाता है। यदि आप javap कमांड के साथ पर्सन $ .class को अलग करते हैं, तो आप देखेंगे कि इसमें कई अन्य लोगों के साथ-साथ एक आवेदन विधि भी शामिल है:

$ javap Person$
Compiled from "Person.scala"
public final class Person$ extends scala.runtime.AbstractFunction1 implements scala.ScalaObject,scala.Serializable{
public static final Person$ MODULE$;
public static {};
public final java.lang.String toString();
public scala.Option unapply(Person);
public Person apply(java.lang.String); // the apply method (returns a Person) public java.lang.Object readResolve();
        public java.lang.Object apply(java.lang.Object);
    }

आप यह देखने के लिए कि क्या इसमें क्या है। इस तरह एक साधारण वर्ग के लिए, इसमें 20 अतिरिक्त तरीके शामिल हैं; यह छिपा हुआ ब्लोट एक कारण है जो कुछ डेवलपर्स को केस क्लासेस पसंद नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.