2016-06-11 2 views
5

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

जैसे:

trait Breed 
case object Pincher extends Breed 
case object Haski extends Breed 

trait Foox{ 
    def age: Int 
    def addToAge(i: Int): Foox 
} 

case class Dog(breed: Breed, age: Int) extends Foox 
case class Person(name: String, age: Int) extends Foox 

मैं चाहता हूँ कि addToAge अतिरिक्त पूर्णांक के साथ एक ही वस्तु वापस आ जाएगी, निश्चित रूप से मैं प्रत्येक वर्ग है, जो सूखी नियम के विपरीत है के लिए एक ही लागू कर सकते हैं:

case class Dog(breed: Breed, age: Int) extends Foox{ 
    def addToAge(i: Int) = copy(age = age + i) 
} 
case class Person(name: String, age: Int) extends Foox{ 
    def addToAge(i:Int) = copy(age = age + i) 
} 
  1. इससे बचने के लिए एक बेहतर तरीका है?

  2. क्या उस उम्र को फिर से परिभाषित करने से बचने का एक विकल्प है: प्रत्येक मामले वर्ग में int और इसकी स्थिति बनाए रखें (उम्र पहले से ही विशेषता में परिभाषित है)?

+0

ठीक है, मैं इसके बारे में वास्तव में उत्सुक हूं। यह प्रश्न कुछ हद तक समान है (लेकिन इसका डुप्लिकेट नहीं) [http://]] (http://stackoverflow.com/questions/7227641/scala-how-can-i-make-my-immutable-classes-easier-to-subclass) और [यह] (http://stackoverflow.com/questions/21560470/method-inheritance-in-immutable-classes)। क्या किसी को पता है कि क्या दिए गए उत्तर इस प्रश्न के साथ बिल्कुल मदद करते हैं? –

+0

यदि आप एक ही ऑब्जेक्ट की स्थिति को बदलने का प्रयास करते हैं तो आप अपरिवर्तनीय सिद्धांत का उल्लंघन कर रहे हैं। इसलिए 'कॉपी' दृष्टिकोण की तरह लगता है। राज्य को इन ऑब्जेक्ट्स का उपयोग करने वाले कोड द्वारा बनाए रखा जाना चाहिए यानी 'addToAge' – tuxdna

+0

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

उत्तर

5

एक संभव समाधान, कि कुछ उपयोग के मामलों को कवर कर सकते, shapeless पुस्तकालय से Lens तों उपयोग करने के लिए है:

import shapeless._ 

abstract class Foox[T](
    implicit l: MkFieldLens.Aux[T, Witness.`'age`.T, Int] 
) { 
    self: T => 
    final private val ageLens = lens[T] >> 'age 

    def age: Int 
    def addToAge(i: Int): T = ageLens.modify(self)(_ + i) 
} 

case class Dog(breed: Breed, age: Int) extends Foox[Dog] 
case class Person(name: String, age: Int) extends Foox[Person] 

ध्यान दें कि एक Lens आप एक अंतर्निहित MkFieldLens की जरूरत बनाने के लिए, तो यह आसान है के बजाय के रूप में Foox को परिभाषित करने के लिए। अन्यथा आपको यह सुनिश्चित करने के लिए हर बच्चे में कुछ कोड लिखना होगा।

इसके अलावा, मुझे नहीं लगता कि प्रत्येक बच्चे में age: Int को परिभाषित करने से बचने का एक तरीका है। जब आप एक उदाहरण बनाते हैं, तो आपको उम्र को कुछ तरीकों से प्रदान करना होगा, उदा। Dog(Pincher, 5), तो आपको उस उम्र के लिए उस कन्स्ट्रक्टर तर्क होना चाहिए।


कुछ और स्पष्टीकरण: एक Haskell Lens tutorial से

उधार:

एक लेंस कुछ डेटा प्रकार के एक subpart के लिए एक प्रथम श्रेणी संदर्भ है। [...] एक लेंस को देखते हुए वहाँ अनिवार्य रूप से तीन बातों subpart

  • बदल रहा एक और लेंस के साथ इस लेंस कम्बाइन से आप subpart
    1. देखें करने के लिए पूरे संशोधित करना चाह सकते हैं गहराई से देखने के लिए

    पहला और दूसरा इस विचार को जन्म देता है कि लेंस प्राप्तकर्ता और आपके जैसे सेटर्स किसी ऑब्जेक्ट पर हो सकते हैं।

  • संशोधन भाग का उपयोग age के साथ हम क्या करना चाहते हैं, इसे लागू करने के लिए किया जा सकता है।

    बेकार पुस्तकालय केस वर्ग क्षेत्रों के लिए लेंस को परिभाषित करने और उपयोग करने के लिए एक सुंदर, बॉयलरप्लेट-मुक्त वाक्यविन्यास प्रदान करता है। The code example in the documentation आत्म व्याख्यात्मक है, मुझे विश्वास है।

    age क्षेत्र के लिए निम्न कोड को उस उदाहरण से इस प्रकार है:

    final private val ageLens = lens[???] >> 'age 
    def age: Int 
    def addToAge(i: Int): ??? = ageLens.modify(self)(_ + i) 
    

    addToAge की वापसी प्रकार क्या होना चाहिए? यह उप-वर्ग का सटीक प्रकार होना चाहिए जिससे इस विधि को बुलाया जा रहा है। यह आमतौर पर F-bounded polymorphism के साथ हासिल किया जाता है। इसलिए हम निम्नलिखित है:

    trait Foox[T] { self: T => // variation of F-bounded polymorphism 
    
        final private val ageLens = lens[T] >> 'age 
    
        def age: Int 
        def addToAge(i: Int): T = ageLens.modify(self)(_ + i) 
    } 
    

    T बच्चे का सही प्रकार के रूप में वहाँ प्रयोग किया जाता है, और हर वर्ग Foox[T] विस्तार (स्वयं प्रकार घोषणा self: T => की वजह से) T के रूप में प्रदान करना चाहिए। उदाहरण के लिए:

    case class Dog(/* ... */) extends Foox[Dog] 
    

    अब हमें lens[T] >> 'age लाइन कार्य करने की आवश्यकता है।

    की क्या यह कार्य करने के लिए की जरूरत है यह देखने के लिए >> विधि के हस्ताक्षर का विश्लेषण करते हैं:

    def >>(k: Witness)(implicit mkLens: MkFieldLens[A, k.T]): Lens[S, mkLens.Elem] 
    
    1. हम देखते हैं कि 'age तर्क परोक्ष एक shapeless.Witness में परिवर्तित हो जाता। Witness एक विशिष्ट मान के सटीक प्रकार का प्रतिनिधित्व करता है, या दूसरे शब्दों में एक प्रकार-स्तरीय मान का प्रतिनिधित्व करता है। दो अलग-अलग अक्षर, उदा। Symbol एस 'age और 'foo, अलग-अलग गवाह हैं और इस प्रकार उनके प्रकारों को प्रतिष्ठित किया जा सकता है।

      बेकार कुछ मूल्य के Witness प्राप्त करने के लिए एक फैंसी बैकटिक वाक्यविन्यास प्रदान करता है। 'age प्रतीक के लिए:

      Witness.`'age` // Witness object 
      Witness.`'age`.T // Specific type of the 'age symbol 
      
    2. मद 1 से बाद और >> हस्ताक्षर, हम, एक अंतर्निहित MkFieldLens उपलब्ध है की जरूरत वर्ग T (बच्चे case class) और क्षेत्र 'age के लिए:

      MkFieldLens[T, Witness.`'age`.T] 
      

      age फ़ील्ड में Int टाइप होना चाहिए। यह निराकार में साथ Aux pattern आम इस आवश्यकता को व्यक्त करने के लिए संभव है:

      MkFieldLens.Aux[T, Witness.`'age`.T, Int] 
      

    और स्वाभाविक रूप से इस अंतर्निहित अधिक प्रदान करने के लिए, एक अंतर्निहित तर्क के रूप में, हम एक trait के बजाय एक abstract class उपयोग करना होगा।

    +0

    यह मेरे मन में समान था। क्या हम किसी भी प्रतिकृति में इसे पुन: उत्पन्न करने के लिए आसान बनाने के लिए आयात भी जोड़ सकते हैं? – marios

    +1

    @marios ठीक है, जोड़ा गया। 'shapeless._' काम करने के लिए पर्याप्त है। आमतौर पर इसे मूल प्रश्न से 'नस्ल' परिभाषा की आवश्यकता होती है। – Kolmar

    +0

    @ कोलमार, धन्यवाद, जो मैं खोज रहा था, क्या आप कृपया इस वूडू के बारे में कुछ और बता सकते हैं :)? – igx

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