2012-10-03 18 views
69

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

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

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

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

What are the problems and pitfalls I should be aware in inheriting from a case class? Does it make sense in my case to do so?

+1

क्या आप गैर-केस क्लास से उत्तराधिकारी नहीं हो सकते हैं, या एक सामान्य विशेषता का विस्तार नहीं कर सकते हैं? – Eduardo

+0

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

+0

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

उत्तर

89

कोड दोहराव के बिना मामले वर्ग विरासत से बचने का मेरे पसंदीदा तरीका कुछ हद तक स्पष्ट है: आप और अधिक सुक्ष्म होना चाहते हैं

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 
+48

यह "कोड डुप्लिकेशन के बिना" कहां है? हां, केस क्लास और उसके माता-पिता के बीच एक अनुबंध परिभाषित किया गया है, लेकिन आप अभी भी प्रो 2 एक्स – virtualeyes

+2

@Virtualeyes ट्रू टाइप कर रहे हैं, आपको अभी भी गुण दोहराना होगा। आपको विधियों को दोहराने की ज़रूरत नहीं है, हालांकि, आमतौर पर गुणों की तुलना में अधिक कोड की मात्रा होती है। –

+1

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

12

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

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

class Point(x : Int, y : Int) 

और

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

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

अद्यतन

आप समस्याओं को सुलझाने के बहुत सारे एक और जवाब में वर्णित के रूप लक्षण से विरासत का उपयोग कर सकते हैं। प्रकार वर्गों का उपयोग करने के लिए अक्सर एक और अधिक लचीला विकल्प होता है। देखें What are type classes in Scala useful for? या http://www.youtube.com/watch?v=sVMES4RZF-8

+0

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

+0

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

+0

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

36

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

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

// 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 

हाँ, आप क्षेत्रों नकल करने की है। यदि आप नहीं करते हैं, तो यह सही समानता among other problems लागू करना संभव नहीं होगा।

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

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

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

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

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

संरचना एक वैध और एक ध्वनि रणनीति है कि आप पर भी विचार करना चाहिए।

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

warning: match is not exhaustive! 
missing combination   Tourist 

कौन वास्तव में उपयोगी है:

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

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

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

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

+1

मुझे लगता है कि विशेषता में 'डीफ़ नाम' 'वैल नाम 'होना चाहिए। मेरा कंपाइलर मुझे पूर्व के साथ पहुंचने योग्य कोड चेतावनी दे रहा था। – BAR

3

इन स्थितियों में मैं भाग अर्थात

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") 
} 

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

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