स्काला केस क्लास इनहेरिटेंस


88

मेरे पास स्क्वेरिल पर आधारित एक आवेदन है। मैं अपने मॉडल को केस क्लासेस के रूप में परिभाषित करता हूं, क्योंकि ज्यादातर मुझे कॉपी विधियों के लिए सुविधाजनक लगता है।

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

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

मैं जो करना चाहूंगा, वह पूर्वजों के मामले में सामान्य व्यवहार और क्षेत्रों का कारक है और इसके पास दो वास्तविक मॉडल हैं। लेकिन, जहां तक ​​मैं समझता हूं, केस क्लासेस से विरासत में मिला स्कैला पर फेंक दिया जाता है, और यहां तक ​​कि निषिद्ध है यदि उपवर्ग स्वयं एक केस क्लास है (मेरा केस नहीं)।

केस क्लास से विरासत में मुझे क्या समस्याएं और नुकसान होने चाहिए? क्या ऐसा करने के लिए मेरे मामले में इसका कोई मतलब है?


1
क्या आप एक गैर-मामले वर्ग से विरासत में नहीं मिले, या एक सामान्य विशेषता का विस्तार कर सकते हैं?
एडुआर्डो

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

मुझे नहीं लगता है, आपको अमूर्त माता-पिता (या विशेषता) और लक्ष्य मामले वर्ग दोनों में सहारा को परिभाषित करना होगा। अंत में, लॉट्स ओ 'बॉयलरप्लेट, लेकिन टाइप कम से कम सुरक्षित
virtualeyes

जवाबों:


118

कोड दोहराव के बिना केस क्लास इनहेरिटेंस से बचने का मेरा पसंदीदा तरीका कुछ हद तक स्पष्ट है: एक सामान्य (सार) बेस क्लास बनाएं:

abstract class Person {
  def name: String
  def age: Int
  // address and other properties
  // methods (ideally only accessors since it is a case class)
}

case class Employer(val name: String, val age: Int, val taxno: Int)
    extends Person

case class Employee(val name: String, val age: Int, val salary: Int)
    extends Person


यदि आप अधिक बारीक होना चाहते हैं, तो गुणों को व्यक्तिगत लक्षणों में समूहित करें:

trait Identifiable { def name: String }
trait Locatable { def address: String }
// trait Ages { def age: Int }

case class Employer(val name: String, val address: String, val taxno: Int)
    extends Identifiable
    with    Locatable

case class Employee(val name: String, val address: String, val salary: Int)
    extends Identifiable
    with    Locatable

83
यह "बिना कोड दोहराव" के आप कहाँ बोलते हैं? हां, केस क्लास और उसके माता-पिता के बीच एक अनुबंध परिभाषित किया गया है, लेकिन आप अभी भी टाइप कर रहे हैं X2
virtualeyes

5
@virtualeyes सच है, आपको अभी भी गुणों को दोहराना है। हालांकि, आपको विधियों को दोहराना नहीं पड़ता है, जो आमतौर पर गुणों की तुलना में अधिक कोड की मात्रा होती है।
माल्टे श्वार्फ

1
हाँ, बस संपत्तियों के दोहराव के आसपास काम करने की उम्मीद कर रहा था - एक संभावित समाधान के रूप में टाइप कक्षाओं में एक और उत्तर संकेत; हालांकि, यह सुनिश्चित नहीं है कि व्यवहार में मिश्रण की तरह अधिक गियर लगता है, जैसे लक्षण, लेकिन अधिक लचीला। बस बॉयलरप्लेट पुनः: केस क्लासेस, इसके साथ रह सकते हैं, यह बहुत अविश्वसनीय होगा यदि यह अन्यथा थे, तो वास्तव में संपत्ति की परिभाषाओं के महान swaths हैक कर सकते हैं
virtualeyes 12

1
@virtualeyes मैं पूरी तरह से सहमत हूं कि यह बहुत अच्छा होगा यदि संपत्ति पुनरावृत्ति को आसान तरीके से टाला जा सके। एक संकलक प्लगइन निश्चित रूप से चाल कर सकता है, लेकिन मैं इसे एक आसान तरीका नहीं कहूंगा।
माल्टे शेवर्हॉफ़

13
@virtualeyes मुझे लगता है कि कोड के दोहराव से बचना केवल कम लिखने के बारे में नहीं है। मेरे लिए, यह आपके आवेदन के विभिन्न हिस्सों में उनके बीच किसी भी कनेक्शन के बिना एक ही कोड नहीं होने के बारे में अधिक है। इस समाधान के साथ, सभी उप-वर्ग एक अनुबंध से बंधे होते हैं, इसलिए यदि माता-पिता वर्ग बदलते हैं, तो आईडीई आपको उस कोड के हिस्सों की पहचान करने में मदद कर सकेगा जिन्हें आपको ठीक करने की आवश्यकता है।
डैनियल

40

चूंकि यह कई लोगों के लिए एक दिलचस्प विषय है, मुझे यहां कुछ प्रकाश डालना चाहिए।

आप निम्नलिखित दृष्टिकोण के साथ जा सकते हैं:

// You can mark it as 'sealed'. Explained later.
sealed trait Person {
  def name: String
}

case class Employee(
  override val name: String,
  salary: Int
) extends Person

case class Tourist(
  override val name: String,
  bored: Boolean
) extends Person

हां, आपको खेतों की नकल करनी होगी। यदि आप ऐसा नहीं करते हैं, तो अन्य समस्याओं के बीच सही समानता को लागू करना संभव नहीं होगा ।

हालाँकि, आपको विधियों / कार्यों की नकल करने की आवश्यकता नहीं है।

यदि कुछ गुणों का दोहराव आपके लिए इतना महत्व रखता है, तो नियमित कक्षाओं का उपयोग करें, लेकिन याद रखें कि वे एफपी को अच्छी तरह से फिट नहीं करते हैं।

वैकल्पिक रूप से, आप विरासत के बजाय रचना का उपयोग कर सकते हैं:

case class Employee(
  person: Person,
  salary: Int
)

// In code:
val employee = ...
println(employee.person.name)

रचना एक मान्य और एक ध्वनि रणनीति है जिसे आपको भी विचार करना चाहिए।

और मामले में आप आश्चर्यचकित हैं कि एक मुहरबंद विशेषता का क्या मतलब है - यह एक ऐसी चीज है जिसे केवल एक ही फ़ाइल में बढ़ाया जा सकता है। यही है, ऊपर के दो मामले वर्गों को एक ही फाइल में होना चाहिए। यह संपूर्ण संकलक जांच के लिए अनुमति देता है:

val x = Employee(name = "Jack", salary = 50000)

x match {
  case Employee(name) => println(s"I'm $name!")
}

एक त्रुटि देता है:

warning: match is not exhaustive!
missing combination            Tourist

जो वास्तव में उपयोगी है। अब आप अन्य प्रकार के Personलोगों (लोगों) के साथ व्यवहार करना नहीं भूलेंगे । यह अनिवार्य रूप Optionसे स्काला में वर्ग करता है।

यदि आपके लिए यह कोई मायने नहीं रखता है, तो आप इसे गैर-सील कर सकते हैं और केस की कक्षाओं को अपनी फाइलों में फेंक सकते हैं। और शायद रचना के साथ चलते हैं।


1
मुझे लगता है कि def nameविशेषता में होना चाहिए val name। मेरा संकलक मुझे पूर्व के साथ अनुपलब्ध कोड चेतावनी दे रहा था।
BAR

13

केस क्लास वैल्यू ऑब्जेक्ट्स के लिए एकदम सही हैं, यानी ऐसी वस्तुएँ जो किसी भी गुण को नहीं बदलती हैं और उनकी तुलना बराबरी से की जा सकती है।

लेकिन विरासत की उपस्थिति में बराबरी को लागू करना बल्कि जटिल है। एक दो वर्गों पर विचार करें:

class Point(x : Int, y : Int)

तथा

class ColoredPoint( x : Int, y : Int, c : Color) extends Point

इसलिए परिभाषा के अनुसार ColorPoint (1,4, लाल) बिंदु के बराबर होना चाहिए (1,4) वे सभी के बाद समान बिंदु हैं। तो ColorPoint (1,4, नीला) भी पॉइंट (1,4) के बराबर होना चाहिए, है ना? लेकिन निश्चित रूप से ColorPoint (1,4, लाल) को ColorPoint (1,4, नीला) के बराबर नहीं होना चाहिए, क्योंकि उनके अलग-अलग रंग हैं। वहां आप जाते हैं, समानता के संबंध की एक बुनियादी संपत्ति टूट जाती है।

अपडेट करें

आप अन्य उत्तर में वर्णित समस्याओं के समाधान के लक्षणों से विरासत का उपयोग कर सकते हैं। एक और अधिक लचीला विकल्प अक्सर प्रकार की कक्षाओं का उपयोग करने के लिए होता है। देखें कि स्काला में किस प्रकार की कक्षाएं उपयोगी हैं? या http://www.youtube.com/watch?v=sVMES4RZF-8


मैं समझता हूं और इससे सहमत हूं। तो, आपको क्या सुझाव देना चाहिए जब आपके पास एक ऐसा आवेदन होता है जो नियोक्ता और कर्मचारियों से कहता है। मान लें कि वे सभी फ़ील्ड (नाम, पता, और इसी तरह) साझा करते हैं, एकमात्र अंतर कुछ तरीकों में है - उदाहरण के लिए एक को परिभाषित करना चाह सकते हैं Employer.fire(e: Emplooyee)लेकिन दूसरे तरीके से नहीं। मैं दो अलग-अलग कक्षाएं बनाना चाहूंगा, क्योंकि वे वास्तव में अलग-अलग वस्तुओं का प्रतिनिधित्व करते हैं, लेकिन मैं उस पुनरावृत्ति को भी नापसंद करता हूं जो उत्पन्न होती है।
एंड्रिया

यहाँ प्रश्न के साथ टाइप क्लास दृष्टिकोण का एक उदाहरण मिला है? यानी केस क्लासेस के संबंध में
virtualeyes

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

@JensSchauder यह प्रतीत होता है कि लक्षण व्यवहार के संदर्भ में एक ही चीज़ प्रदान करते हैं, प्रकार की कक्षाओं की तुलना में कम लचीला; मैं केस क्लास प्रॉपर्टीज़ के नॉन-डुप्लीकेशन पर पहुँच रहा हूँ, कुछ ऐसा जो ट्रेस या एब्स्ट्रैक्ट क्लासेस आम तौर पर किसी को बचने में मदद करेगा।
virtualeyes

7

इन स्थितियों में मैं विरासत के बजाय रचना का उपयोग करता हूं

sealed trait IVehicle // tagging trait

case class Vehicle(color: String) extends IVehicle

case class Car(vehicle: Vehicle, doors: Int) extends IVehicle

val vehicle: IVehicle = ...

vehicle match {
  case Car(Vehicle(color), doors) => println(s"$color car with $doors doors")
  case Vehicle(color) => println(s"$color vehicle")
}

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


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