2012-09-04 31 views
12

शायद स्टाइल और लालित्य की अच्छी समझ के साथ एक स्कैला विशेषज्ञ मुझे निम्न कोड को संरचित करने के लिए एक अच्छा तरीका समझने में मदद कर सकता है, जिसमें एक निर्माता "पुश-आउट" समस्या है।मॉड्यूलर स्कैला डिज़ाइन: मैं कन्स्ट्रक्टर "पुश-आउट" बॉयलरप्लेट से कैसे बचूं?

हम एक सरल आधार वर्ग के साथ शुरू:

class Foo(val i: Int, val d: Double, val s: String) { 

    def add(f: Foo) = new Foo(i + f.i, d + f.d, s + f.s) 
    override def toString = "Foo(%d,%f,%s)".format(i,d,s) 

} 

एक जटिल आवेदन में प्रकार की जाँच प्रयोजनों के लिए, मैं किसी भी अतिरिक्त राज्य के बिना एक उप-वर्ग की आवश्यकता होती है:

class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) { 

    override def toString = "Bar(%d,%f,%s)".format(i,d,s) 

} 

यह खड़ा के रूप में , जब मैं दो बार्स जोड़ता हूं, तो मुझे केवल एक फू वापस मिलता है:

val x = new Bar(1,2.3,"x") 
val y = new Bar(4,5.6,"y") 
val xy = x.add(y) 

में निम्नलिखित प्रतिक्रिया के साथ आरईपीएल:

x : Bar = Bar(1,2.300000,x) 
y : Bar = Bar(4,5.600000,y) 
xy : Foo = Foo(5,7.900000,xy) 

मैं कैसे नीचे के रूप में दो बार एक साथ जोड़ने के लिए एक और बार (बजाय एक फू से) बनाने के लिए, एक सुरुचिपूर्ण ढंग से, कॉपी और फू के ऐड विधि पेस्ट किए बिना मिलता है,?

class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) { 

    // ugly copy-and-paste from Foo: 
    def add(b: Bar) = new Bar(i + b.i, d + b.d, s + b.s) 
    override def toString = "Bar(%d,%f,%s)".format(i,d,s) 

} 

मैं कई ऐसे बार्स (सभी अनिवार्य रूप से फू की प्रतियां, लेकिन प्रकार की जाँच के लिए बहुत महत्वपूर्ण है), एक कट और पेस्ट से मुक्त समाधान लाभांश का भुगतान करेगा।

धन्यवाद!

उत्तर

12

मैं जितना संभव हो सके विरासत से बचने की कोशिश करता हूं। तो यहां एक वैकल्पिक दृष्टिकोण है।

कक्षा BarFoo से बिल्कुल वही निर्माता है और दोनों स्टेटलेस हैं। यदि आप कई उप-प्रकार चाहते हैं, तो बस किसी भी अतिरिक्त जानकारी को व्यक्त करने के लिए, आप एक सामान्य पैरामीटर का उपयोग "लेबल" के रूप में कर सकते हैं। उदाहरण के लिए:

trait Kind 
trait Bar extends Kind 

class Foo[T<:Kind](val i: Int, val d: Double, val s: String) { 
    def add(f: Foo[T]) = new Foo[T](i + f.i, d + f.d, s + f.s) 
    override def toString = "Foo(%d,%f,%s)".format(i,d,s) 
} 


scala> val b1 = new Foo[Bar](2,3.0,"hello") 
b1: Foo[Bar] = Foo(2,3.000000,hello) 

scala> val b2 = new Foo[Bar](3,1.0," world") 
b2: Foo[Bar] = Foo(3,1.000000, world) 

scala> b1 add b2 
res1: Foo[Bar] = Foo(5,4.000000,hello world) 

अब add टाइप सुरक्षित है। Kind प्रदर्शित करने के लिए आप toString प्राप्त करने के लिए एक प्रकार की कक्षा का उपयोग कर सकते हैं।

+0

मैं विचार "लेबल के रूप में पैरामीटर", प्रकार की सुरक्षा के लिए की तरह है: तो, एक वैकल्पिक दृष्टिकोण एक ओवरराइड करने योग्य कारखाने विधि (जो केवल निर्माता के लिए एक प्रतिनिधि है) हो सकता है। धन्यवाद! –

5

@ के निदर्शनात्मक जवाब पर विस्तार, यदि आप कार्य है कि प्रत्येक Bar (जैसे विभिन्न toString) के लिए विशिष्ट हैं समर्थन करने के लिए सक्षम होना चाहते हैं, तो आप एक कदम आगे जाने के लिए और Kind एक typeclass बना सकते हैं।

trait Kind[T] { def name : String } 
trait Bar 
implicit object BarHasKind extends Kind[Bar] { val name = "Bar" } 

class Foo[T : Kind](val i : Int, val d : Double, val s : String) { 
    def add(f : Foo[T]) = new Foo[T](i + f.i, d + f.d, s + f.s) 
    override def toString = implicitly[Kind[T]].name + "(%d,%f,%s)".format(i,d,s) 
} 

scala> val b1 = new Foo[Bar](2, 3.0, "hello") 
b1: Foo[Bar] = Bar(2,3.000000,hello) 

trait Biz 
implicit object BizHasKind extends Kind[Biz] { val name = "Biz" } 

scala> val b2 = new Foo[Biz](1, 1.0, "One") 

यह पहले की तरह सिर्फ प्रकार के रूप में सुरक्षित है:

scala> b1 add b2 
<console>:16: error: type mismatch; 
    found : Foo[Biz] 
    required: Foo[Bar] 

scala> b2 add b2 
resN: Foo[Biz] = Biz(2,2.000000,OneOne) 

किसी भी संपत्ति है कि आप टैग पर निर्भर होना चाहते हैं, उस उन्हें Kind में संक्षेप में घोषित करने और अंतर्निहित वस्तुओं में कार्यान्वयन प्रदान करते हैं।

+0

दिलचस्प। फू के अंदर 'def name = "foo" विधि घोषित करने पर आपके "निहित रूप से" दृष्टिकोण का क्या फायदा है, और बार और बिज़ इसे ओवरराइड करते हैं? –

+1

यह वास्तव में एक लाभ नहीं है, मैं बस यह दिखाने की कोशिश कर रहा था कि आप किसी भी विरासत के साथ एक सेटिंग में भी स्वीकार कर सकते हैं, स्वीकृत उत्तर में प्रस्तावित एक के समान। – Philippe

2

टाइप पैरामीट्रिसेशन के साथ दृष्टिकोण में एक सीमा है जिसमें यह आपको प्राकृतिक तरीके से कार्यक्षमता बढ़ाने की अनुमति नहीं देता है।

class Foo(val i: Int, val d: Double, val s: String) { 

    protected def create(i: Int, d: Double, s: String) = new Foo(i, d, s) 

    def add[A <: Foo](f: A) = create(i + f.i, d + f.d, s + f.s) 

    override def toString = "Foo(%d,%f,%s)".format(i,d,s) 
} 

class Bar(i: Int, d: Double, s: String) extends Foo(i,d,s) { 

    protected override def create(i: Int, d: Double, s: String) = new Bar(i, d, s) 

    override def toString = "Bar(%d,%f,%s)".format(i,d,s) 

    // additional methods... 

} 

println(new Foo(10, 10.0, "10") add new Bar(10, 10.0, "10")) 
println(new Bar(10, 10.0, "10") add new Foo(10, 10.0, "10"))