当一个组件(component)需要其他接口或组件的支持时,应该如何表达?答案是将这些接口或组件进行抽象处理.
从编程语言角度来看,参数化(parameterization)和抽象成员(abstract member)是两种主要的抽象形式。其中参数化是函数式(functional)所表达的观点,抽象成员是面向对象(Object-Oriented)所表达的观点。由于Scala同时支持函数式和面向对象两种编程风格,因此Scala都支持这两种抽象形式。事实上,在Scala中参数化是通过抽象成员的形式进行表达。
抽象又可以分为类型抽象(abstract type)和值抽象(abstract value). 不管是哪种抽象形式,都能被参数化或作为类的抽象成员(abstract member)进行表达。
先看一个例子
abstract class AbsCell { type T // T is abstract type member val init: T // init is abstract value member private var value = init def get: T = value def set(x: T) = { value = x } }
AbsCell是一个抽象类,其中T是抽象类型成员, init是抽象值成员。如果需要实例化AbsCell, 必须同时给出T和init的具体定义:
val cell = new AbsCell{ type T = Int val init =1 } cell.set(cell.get * 2);
这里cell的类型是AbsCell{ type T = Int}。 但是编译后,type T =Int会被擦除(erasion).
println(cell.isInstanceOf[AbsCell{ type T=String}]); // true val cell2 : AbsCell{ type T = String } = cell ; // compilation error.
接下来讲讲抽象类型成员(abstract type member)在组件解耦方面的运用。
假设需要实现两个组件,不妨记为A组件和B组件。这两个组件互相依赖:
// A组件 object Types{ abstract class Type{ def sym: Symbols.Symbol // 依赖B组件的Symbols.Symbol类 } }
// B组件 object Symbols{ abstract class Symbol{ def tpe : Types.Type // 依赖A组件的Types.Type类 } }
由于两个组件互相依赖,因此无法单独编译。一种解决办法是将这两个组件的接口进行合并
// C组件 class SymbolTable{ object Symbols{ abstract class Symbol{ def tpe: Types.Type } } object Types{ abstract class Type{ def sym: Symbols.Symbol } } }
这样A,B,C组件就可以单独编译(A,B, C组件不存在其他依赖)
这种方法存在两个问题:
1) 随着组件的增多,可能会出现大量像C组件这样的中间组件(或粘合组件)
2)如果A,B组件分别由两个团队开发,那么这种解决办法是低效的。因为需要将两个组件互相依赖的代码抽取出来,然后融合到一个文件或类中。这种沟通和协调成本往往代价高昂。
抽象类型成员提供了另一种解决思路
// A组件 trait Types{ type Symbol // 声明一个抽象类型 abstract class Type { def sym: Symbol } }
// B组件 trait Symbols{ type Type // 声明一个抽象类型 abstract class Symbol{ def tpe : Type } }
这样A,B组件就能单独编译。 使用混入(mixin)方式将两个组件的接口组合起来
// 客户端组件 class SymbolTable extends AnyRef with Types with Symbols{ override type Type = Types#Type // 由于抽象类型成员Type和Types中的抽象类Type同名,因此可以省略此处的类型定义 // override type Symbol = String override type Symbol = Symbols#Symbol // 同上 class SubType extends super.Type{ // def sym = new String("abc") // if Symbol is String def sym = new SubSymbol() } class SubSymbol extends super.Symbol{ def tpe = new SubType } def demo(){ println(new SubType().sym) println(new SubSymbol().tpe) } }
抽象类型是解决组件间互相依赖的有效手段.