Scala OOP 基础三

2017/11/12 Scala

Scala OOP 基础知识三

trait 特征

类似与java中的interface,但是又有不同之处。

java 中的接口

Java 8 做出了改变。现在我们可以在接口中定义方法,这些方法被称为 defender 方法或默认方法。实现类仍可以提供自己的实现。如果实现类未提供自己的实现的话,defender 方法会被调用。因此,Java 8 中的接口行为更接近于 Scala 中的 trait。 Java 8 中的接口与 Scala 中的 trait 仍有不同之处。Java 8 中的接口只能定义静态字段,而 Scala 中的 trait 则可以定义实例级字段。这意味着 Java 8 中的接口无法管理实例状态。接口实现类必须提供字段以记录状态。这也意味着 defender 方法无法访问接口实现体的状态信息,从而限制了 defender 方法的用途。

混入trait

回调逻辑则很适用于实现成混入结构 (mixin)。这里我举例说明:

//常见的回调事件处理机制
class ButtonWithCallBacks(val label:String,val callbacks:List[()=>Unit]=Nil) extends Widget{
  def click():Unit={
    update()
    callbacks.foreach(f=>f())
  }
  
  protected def update():Unit={/*...*/}
}

Object ButtonWithCallBacks {
  def apply(label:String, callback:()=>Unit)=new ButtonWithCallbacks(label,List(callback))
  
  def apply(label:String) = new ButtonWithCallbacks(label,Nil)
}
//拆开回调函数
trait Observer[-State]{   //此处的字节码和java的一致,因为接口中什么内容都没有
  def receiveUpdate(state:State):Unit
}

trait Subject[State]{
  private var observers:List[Observer[State]]=Nil  //not thread safe
  def addObserver(observer:Observer[State]):Unit=
  	observers ::= observer				//add observer		
  	
  def notifyObservers(state:State):Unit=
  	observers foreach (_.receiveUpdate(state))    //notify
}

class Button(val label:String) extends Widget{
  def click():Unit=update()
  def update:Unit={/*...*/}
}

class ObservabbleButton(name:String) extends Button(name) with Subject[Button]{  //如果混入的第一个是trait也要用extends
  override def click():Unit={
    super.click()
    notifyObservers(this) //notify observers
  }
}

class ButtonCountObserver extends Observer[Button]{ // 统计点击次数
  var count=0
  def receiveUpdate(state:Button):Unit=count+=1
}

val button1= new Button("click me") with Subject[Button]{ //这是和java差别很大的地方,是scala比java更加灵活的地方,相当于java的匿名内部类了
//实例化对象的时候用with 来引入 trait
  override def click():Unit={
    super.click()
    notifyObserver(this)
  }
}

混入多个trait

这个地方直接上代码来说明:

 trait Clickable{
   def click():Unit = update()
   protected def update():Unit //模版方法模式
 }
 
trait ObservableClicks extends Clickable with Subject[Clickable]{
  abstract override def click():Unit={ 
    super.click()
    notifyObservers(this)
  }//加上abstract 关键字的原因是因为调用了super对象中的另一个没有具体实现的函数,这是指的是Clickable.update()函数 
}
 
val button2=new Button["click"] with ObservableClicks //直接使用即可

trait 的继承

trait默认构造函数不允许提供参数列表,也不能定义构造函数。但是trait可以继承其它的trait和类,但是不可以向父类的构造函数传递参数。trait只能继承有无参构造函数的类。每次创建使用了trait 的实例时,trait都会执行。 如果一个trait或抽象父类中存在抽象成员,后续的trait必须定义这些成员,具体类中不能包含抽象成员。

使用类或者trait

trait 是scala实现混入的方法,用于大多数的辅助行为。如果发现trait多数时候被用途父类,子类的表现(behave as )trait的话,可以考虑把trait改成类使用。

良好的面向对象设计需要遵循下列的通用原则:一旦完成构造过程,该实例便应一直处于某种已知的合法状态中。

Show Disqus Comments

Search

    Table of Contents