Scala的本地函數:將私有方法轉換為本地方法
上節中processFile方法的建立演示了函數式編程風格的一個重要設計原則:程序應該被解構成若干小的函數,每個完成一個定義良好的任務。單個函數經常很小。這種風格的好處是它給了程序員許多可以靈活組裝成更復雜事物的建造模塊。每個小塊應該充分簡化到足以單獨理解。
這種方式的一個問題是所有這些幫助函數的名稱會污染程序的命名空間。在解釋器里這不太成問題,但是一旦函數被打包成可復用的類和對象,就最好對類的客戶隱藏幫助函數。它們經常不能獨立表達什么意思,并且如果之后用其它方式重寫類的話,也常會想保持能刪掉幫助方法的足夠的靈活度。
Java里,達成這個目的的主要工具是private方法。這種私有方法的方式在Scala里同樣有效,如代碼8.1里描述的,但是Scala提供了另一種方式:你可以把函數定義在另一個函數中。就好象本地變量那樣,這種本地函數僅在包含它的代碼塊中可見。以下是一個例子:
在這個例子中,我們通過把私有方法,processLine,轉換為本地方法,processFile,重構了展示在代碼8.1中原本的LongLines版本。為了做到這點我們去掉了private修飾符,它僅能應用于方法(并且僅被方法需要),然后把processLine的定義放在processFile的定義里。作為本地函數,processLine的范圍局限于processFile之內,外部無法訪問。
- def processFile(filename: String, width: Int) {
- def processLine(filename:String, width:Int, line:String) {
- if (line.length > width) print(filename+": "+line)
- }
- val source = Source.fromFile(filename)
- for (line < - source.getLines) {
- processLine(filename, width, line)
- }
- }
既然processLine被定義在processFile里,另一個改善變為可能了。請注意filename和width是怎樣不改變地傳入到幫助函數中。這不是必須的,因為本地函數可以訪問包含它們的函數的參數。你可以直接使用外部processLine函數的參數,如代碼8.2所示:
代碼 8.2 帶本地processLine方法的LongLines
- import scala.io.Source
- object LongLines {
- def processFile(filename: String, width: Int) {
- def processLine(line: String) {
- if (line.length > width)
- print(filename +": "+ line)
- }
- val source = Source.fromFile(filename)
- for (line < - source.getLines)
- processLine(line)
- }
- }
更簡單了,不是嗎?這種對外層函數的參數的使用是Scala提供的通用嵌套的很平常也很有用的例子。7.7節描述的嵌套和作用域應用于所有的Scala架構,包括函數。這是一個簡單的原則,不過非常強大,尤其在擁有函數作為第一類值的語言中。
【相關閱讀】
- Scala中定義函數的方法:method
- 繼續領悟函數式:Scala指令式風格代碼的重構
- 學習Scala的變量范圍
- Scala:match表達式、break和continue
- 學習Scala:使用try-catch表達式處理異常