-
Scala
中允許你使用package
將程序分為小的部分箭养,每一個(gè)小的部分叫做一個(gè)模塊徐伐,package
無法表示抽象邑彪,也不能被繼承。而且在程序中只能有一種配置惯裕。 - 隨著程序規(guī)模的增大,以模塊化的方式進(jìn)行程序組織十分重要绣硝,可以通過編譯不同的模塊來構(gòu)建系統(tǒng)蜻势,使得不同的小組互不干擾的工作,允許進(jìn)行靈活的插拔互換鹉胖,可以在不同的上下文中使用不同的系統(tǒng)配置握玛。
- 模塊化編程的基本要求:
- 有一個(gè)很好地分離了接口和實(shí)現(xiàn)的模塊結(jié)構(gòu)够傍;
- 有方式可以替換具有相同接口的模塊,不需要改變或者重新編譯依賴該模塊的其他模塊挠铲;
- 有方式可以把模塊連接在一起冕屯。這種連接的任務(wù)可以被認(rèn)為是在配置該系統(tǒng)。其中一種方式是依賴注入拂苹,
Java
中使用的是Spring
技術(shù)安聘,在Scala
中將object
當(dāng)做模塊使用就可以實(shí)現(xiàn)大規(guī)模的程序。
- 有方式可以把模塊連接在一起冕屯。這種連接的任務(wù)可以被認(rèn)為是在配置該系統(tǒng)。其中一種方式是依賴注入拂苹,
-
Scala
使用對象表示模塊瓢棒,所以可以使用對象表示不同的模塊搞挣,比如數(shù)據(jù)庫模塊,應(yīng)用層模塊等音羞。程序可以按照各自的功能被分隔在不同的object
中囱桨,但是目前的recipe數(shù)據(jù)庫
和recipe瀏覽器
是硬連接,因?yàn)樵?code>recipe瀏覽器中直接提到了數(shù)據(jù)庫模塊的名稱
:
SimpleDatabase.allRecipes.filter(recipe => ...
這樣并不能輕易的修改SimpleDatabase
而不影響到瀏覽器模塊嗅绰,瀏覽器模塊需要重新修改和編譯舍肠。當(dāng)使得模塊可插拔的時(shí)候,需要避免代碼重疊窘面,因?yàn)榭赡苡写罅康拇a在相同模塊的不同實(shí)現(xiàn)之間進(jìn)行共享翠语,解決的方式是抽象,模塊是對象财边,模塊的模板就是類肌括。
- 模塊通常都比較大,因而不適合放在單個(gè)文件中酣难,可以使用特質(zhì)把模塊拆分為多個(gè)文件谍夭。
- 如果在特質(zhì)
A
中需要使用特質(zhì)B
中定義的類,可以在A
中使用this
來指定混入A
的類必須是B
憨募,如下所示:
trait SimpleRecipes { // Does not compile
this: SimpleFoods =>
// 可以保證在SimpleFoods中的Pear在這里可以被訪問
object FruitSalad extends Recipe(
"fruit salad",
List(Apple, Pear), // Uh oh
"Mix it all together."
)
def allRecipes = List(FruitSalad)
}
使用的時(shí)候紧索,如果一個(gè)實(shí)現(xiàn)類混入了SimpleRecipes
,則其必須是個(gè)SimpleFoods
class Test extends SimpleRecipes with SimpleFoods
// 如果僅僅繼承了SimpleRecipes是不行的菜谣。
運(yùn)行時(shí)連接
Scala
模塊可以在運(yùn)行時(shí)被連接在一起珠漂,并且還可以根據(jù)運(yùn)行時(shí)的計(jì)算決定將哪些模塊連接起來,其實(shí)也就是自主選擇接口的實(shí)現(xiàn)尾膊∠蔽#可以使用Scala
代碼完成配置,提名需要使用的模塊冈敛,將其連接在一起待笑。使用父類的接口對象將模塊連接在一起可以,當(dāng)修改真正的實(shí)現(xiàn)模塊的時(shí)候莺债,相應(yīng)的依賴模塊并不需要重新編譯滋觉。
object GotApples {
def main(args: Array[String]) = {
val db: Database =
if(args(0) == "student")
StudentDatabase
else
SimpleDatabase
object browser extends Browser {
val database = db
}
val apple = SimpleDatabase.foodNamed("Apple").get
for(recipe <- browser.recipesUsing(apple))println(recipe)
}
}
有時(shí)候會遇到兩種類型是一樣的,但是編譯器不能識別齐邦。
browser.displayCategory(category: Database.Category)
browser.displayCategory(browser.database.allCategories.head) //可以
browser.displayCategory(db.allCategories.head)
GotApples2.scala:14: error: type mismatch;
found : db.FoodCategory
required: browser.database.FoodCategory
browser.displayCategory(category)
編譯器無法理解db
和browser.database
是同一個(gè)物體椎侠,就簡單的人為兩者的類型是不一致的,解決的方法是使用object
措拇,必須明確的通知是使用db.type
我纪,也就是下面的寫法:
val database: db.type = db
-
type
指的是static type
(編譯期類型),class
指的是dynamic type
(運(yùn)行期類型)-
type
使用的都是.
連接丐吓,new 出來的對象的type都是controllers.GotApples.Child
這種形式浅悉,object的type是controllers.GotApples.Child.type這種形式。
-
-
class
使用的都是$
連接券犁,如果是內(nèi)部類术健,是使用$
連接的,如果是object
粘衬,則類為Child$
荞估,$
同時(shí)起到連接和表示object class
的作用,因此一個(gè)val
或者var
的class
中package
是用.
連接的稚新,類的嵌套關(guān)系是用$
連接的勘伺,如果在object Father
中有object Child
,最后的類也是package.Father$Child$
這樣的褂删。不會出現(xiàn)連著兩個(gè)$
的情況飞醉。
-
- 最后在程序中使用內(nèi)部類,作為一個(gè)類型的時(shí)候屯阀,使用的是
Outer#Inner
這樣的寫法缅帘。如果表示一個(gè)object
的類型,使用object.type
难衰。
- 最后在程序中使用內(nèi)部類,作為一個(gè)類型的時(shí)候屯阀,使用的是