scala - composition and inheritance

In this post, we will examine some of the techniques that we will employ to do OO programming or better organize of the Scala apllication.

the topic that we will cover in this post includes:

  1. abstract class and extending class (override and access to base)
  2. parametric field
  3. final field and final class
  4. an example that organize around class and objects.


abstract calss and extending class (override and access to base) 

// abstract_class.scala

// description:
//  abstract_class.scala is the file that demonstrate how to define and use the abstract class

abstract class Element { 
  def contents : Array[String]
  
  // you can define parameterless methods
  
  
  def height: Int = contents.length
  def width : Int = if (height == 0) 0 else  contents(0).length
  
  // one note on the parameter list, instead of the empty parameter notation such as ()
  //  we did not even provide a parameter
  // it was because there is such a convention, in that 
  // if the function does not have side effect and it takes no parameter, we default provide non-parameter list
  // otherwise, we provide a void (empty ) parameter
  // we shall see that later there are some other benefit later. 
  
}

 

abstract class ElementAlternative { 
  def contents : Array[String]
  
  // we can also use val, instead of def
  // the good side is that it is faster, but we may require some additional memory spaces
  // another thing is that if you have a 'pure' def function, it is generally 
  // safe to change that to a val declaration, (because val is generally pure ) 
  val  height : Int = contents.length
  
  val  width : Int = if (height == 0) 0 else  contents(0).length
}


// Extending class
class ArrayElement_pre1(conts: Array[String]) extends Element  {
  def contents : Array[String]  = conts;
}

// however, we have to first come up with a dummy parameter in the constructor, and we only used that parameter
// for initialize the contens property, is there any better way ? 

val ae = new ArrayElement_pre1(Array("Hello", "World"))

ae.width

// that is so typical, that, you may get what the meaning of the following.
val e : Element = new ArrayElement_pre1(Array("Hello", "World"))
e.width

// overriding methods and field
// you can change the def implementation from val definition in the 
// extended class wihtout having to modify the abstract method definition below 
class ArrayElement_pre2(conts : Array[String]) extends Element { 
  val contents : Array[String] = conts
}


// said before that we dislike the idea that 
// the sole purpose of the parameter used to initilalize the contents function
// in fact, "parametric field" such as below will combine  the parameter and the field
// in one definition
// NOTE: there is a "val" before the 'contents" parameter
class ArrayElement_pre3(val contents: Array[String]) extends Element

// it has the same meaning as below 
// class ArrayElement_pre3(x123 : Array[String]) extends  Element  {
//   val contents = Array[String] = x123
// }

class ArrayElement (val contents : Array[String]) extends Element

// with the parametric field, you can do more (override, public, private, var) , as follow 

class Cat {
  val dangerous  = false
}

class Tiger (
  override val dangerous : Boolean,
  private var age : Int

) extends Cat

val tiger = new Tiger(true, 10)
tiger.dangerous

// invoke super
// just place the parameter in the super class after the Extends keyword
class LineElement (s: String) extends ArrayElement(Array(s)) {
  override def width = s.length
  override def height = 1
}

class UniformedElement (
  ch : Char,
  override val width  : Int,
  override val height : Int
    ) extends Element {
  private val line = ch.toString * width
  def contents = Array.fill[String](height)(line)
  
  // be careful that the two calls are indeed different
//  Array.fill[String](height)(line)
//  Array.fill(height)(line)
}

// how to declare a final member
// or a final class
abstract class Element_pre1 {
  def demo() = { println( "Element's implementation invoked")}
}

class ArrayElement_pre4 extends Element_pre1 {
  final override def demo() = { println( "ArrayElement_pre4 implementation invoked")}
}

class LineArrayElement_pre1 extends ArrayElement_pre4 { 
  override def demo() { println("LineArrayElement_pre's implementation invoked")}
}

// you can even make the class final itself 
final class ArrayElement_pre5 extends Element_pre1 {
  override def demo() {
    println("ArrayElement_pre5's implementation inovked")
  }
}

class LineArayElement_pre2 extends ArrayElement_pre5 {
  override def demo() {
    println("LineArayElement_pre2's implementation inovked")
  }
}

// A note on the coding style and convention

// good 
Array(1, 2, 3).toString
"abc".length


Array(1, 2, 3).toString()
"abc".length()

// bad
println
// good
println()
Parametric Field

notice above code like this: 

class ArrayElement_pre3(val contents: Array[String]) extends Element

final field and final class

notice the code above 

class ArrayElement_pre4 extends Element_pre1 {
  final override def demo() = { println( "ArrayElement_pre4 implementation invoked")   }
}
and this

final class ArrayElement_pre5 extends Element_pre1 {
  override def demo() {
    println("ArrayElement_pre5's implementation inovked")
  }
}
An example around classes and objects organization

first, let's define element, where it reprenst a symbol to display.

// elements.scala

// description:
//   a show case on the composition and inheritance


object Element { 
  
  private class ArrayElement(val contents: Array[String]) extends Element 

  
  private class LineElement(
      s : String) extends Element {
          def contents : Array[String] = Array(s)
          override def height : Int = 1
          override def width : Int = s.length
    }
  
  private class UniformElement(
      chr : Char,
      override val width : Int,
      override val height :Int
      ) extends Element {
    
    // ch * width will fail, 
    // and the error message is not clear - type mismatch (expect String, Int provided) the REPL need to improve 
    // on it error handling.
    private val line : String = chr.toString * width
    override def contents : Array[String] = Array.fill(height)(line)
  }
  
  
  def elem(contents: Array[String]) : Element = new ArrayElement(contents)
  
  def elem(s : String) : Element  = new LineElement(s)
  
  def elem(chr : Char, width : Int, height : Int) : Element= new UniformElement(chr, width, height)
  
}


import Element.elem

// contents: not defined, you need to declare your class as "abstract" 
//  
abstract class Element { 
  def contents : Array[String]
  def height : Int = contents.length
  def width = if (contents.length ==0 ) 0 else contents(0).length
  
  
  def beside(that : Element) : Element = {
    val this1 = this heighten that.height
    val that1 = that heighten this.height
    
    elem (
      for ((line1, line2) <- this1.contents zip that1.contents) yield (line1 + line2)    
    )
    
  }
  
  
  def above(that : Element) : Element = {
    val this1 = this widen that.width
    val that1 = that widen this.width
    
    elem(this1.contents ++ that1.contents)
    
  }
  
  def widen(w : Int) : Element = 
    if (w <= width) this
    else {
      val left = elem(' ', (w - width) /2 , height)
      val right = elem(' ', (w - width  + left.width), height)
      
      left beside this beside right
    }
  
  def heighten(h : Int) : Element = 
    if (h <= height) this 
    else {
      val top = elem(' ', width, (h - height) / 2)
      val bottom = elem(' ', width, (h - height + top.height))
      
      top above this above bottom
    }
    
  
  override def toString = contents mkString "\n"
    
}
and we have some code to use the Elemnt to create some Spiral like Element. 

// spiral.scala

// use of the spiral.scala

import Element.elem

object Spiral { 
  val space = elem(" ")
  val corner = elem("+")
  
  def spiral (nEdge : Int, direction :Int) : Element = {
    if (nEdge == 1) 
      elem("+")
    else { 
      val sp = spiral(nEdge - 1, (direction + 3) % 4)
      def verticalBar = elem('|', 1, sp.height)
      def horizontalBar = elem('-', sp.width, 1)
      if (direction == 0) 
        (corner beside horizontalBar ) above (sp beside space)
      else if (direction == 1)
        (sp above space) beside (corner above verticalBar)
      else if (direction == 2)
        (space beside sp) above (horizontalBar beside corner)
     else 
       (verticalBar above corner) beside (space above sp)
    }
  }
  
  def main(args: Array[String]) {
    val nSides = args(0).toInt
    
    println(spiral(nSides, 0))
  }
}

你可能感兴趣的:(scala)