根據(jù)正交設(shè)計(jì)的基本原則,如果設(shè)計(jì)出現(xiàn)重復(fù)的控制邏輯,可抽象出穩(wěn)定的抽象;借助于Scala強(qiáng)大的可擴(kuò)展能力,可以將「小括號(hào)」神奇地轉(zhuǎn)換為「大括號(hào)」,讓用戶代碼感覺是一種新的控制結(jié)構(gòu)。
本文通過一個(gè)簡單的例子,通過若干迭代,遵循正交設(shè)計(jì)的基本原則,靈活地應(yīng)用重構(gòu),逐漸改進(jìn)設(shè)計(jì),以供參考。
需求1:搜索目錄下擴(kuò)展名為
.scala的所有文件
快速實(shí)現(xiàn)
object FileMatchers {
def ends(file: File, ext: String) = {
for (file <- file.listFiles if file.getName.endsWith(ext))
yield filelist
}
}
需求2:搜索目錄下名字包含
Test的所有文件
重復(fù)
object FileMatcher {
def ends(file: File, ext: String) = {
for (file <- file.listFiles if file.getName.endsWith(ext))
yield file
}
def contains(file: File, query: String) = {
for (file <- file.listFiles if file.getName.contains(query))
yield file
}
}
需求3:搜索目錄下名字正則匹配特定模式的所有文件
再現(xiàn)重復(fù)
object FileMatcher {
def ends(file: File, ext: String) = {
for (file <- file.listFiles if file.getName.endsWith(ext))
yield file
}
def contains(file: File, query: String) = {
for (file <- file.listFiles if file.getName.contains(query))
yield file
}
def matches(file: File, regex: String) = {
for (file <- file.listFiles if file.getName.matches(regex))
yield file
}
}
提取抽象
消除消除上述實(shí)現(xiàn)的重復(fù),最重要的是提取公共的關(guān)注點(diǎn): Matcher: (String, String) => Boolean。
object FileMatcher {
private def list(file: File, query: String,
matcher: (String, String) => Boolean) = {
for (file <- file.listFiles if matcher(file.getName, query))
yield file
}
def ends(file: File, ext: String) =
list(file, ext, (fileName, ext) => fileName.endsWith(ext))
def contains(file: File, query: String) =
list(file, query, (fileName, query) => fileName.contains(query))
def matches(file: File, regex: String) =
list(file, regex, (fileName, regex) => fileName.matches(regex))
}
類型推演
借助于Scala強(qiáng)大的類型推演能力,可以得到更為簡潔的函數(shù)字面值。
object FileMatcher {
private def list(file: File, query: String,
matcher: (String, String) => Boolean) = {
for (file <- file.listFiles if matcher(file.getName, query))
yield file
}
def ends(file: File, ext: String) =
list(file, ext, _.endsWith(_))
def contains(file: File, query: String) =
list(file, query, _.contains(_))
def matches(file: File, regex: String) =
list(file, regex, _.matches(_))
}
類型別名
list的參數(shù)由于類型修飾,顯得有點(diǎn)過長而影響閱讀;可以通過「類型別名」的機(jī)制縮短函數(shù)的類型修飾符,以便改善表達(dá)力。
object FileMatcher {
private type Matcher = (String, String) => Boolean
private def list(file: File, query: String, matcher: Matcher) = {
for (file <- file.listFiles if matcher(file.getName, query))
yield file
}
def ends(file: File, ext: String) =
list(file, ext, _.endsWith(_))
def contains(file: File, query: String) =
list(file, query, _.contains(_))
def matches(file: File, regex: String) =
list(file, regex, _.matches(_))
}
簡化參數(shù)
簡化參數(shù)傳遞,消除不必要的冗余,是簡單設(shè)計(jì)基本原則之一。
object FileMatcher {
private type Matcher = String => Boolean
private def list(file: File, matcher: Matcher) = {
for (file <- file.listFiles if matcher(file.getName))
yield file
}
def ends(file: File, ext: String) =
list(file, _.endsWith(ext))
def contains(file: File, query: String) =
list(file, _.contains(query))
def matches(file: File, regex: String) =
list(file, _.matches(regex))
}
替換for comprehension
可以通過定制「高階函數(shù)」替代語法較為復(fù)雜的「for comprehension」,以便改善表達(dá)力。
object FileMatcher {
private type Matcher = String => Boolean
private def list(file: File, matcher: Matcher) =
file.listFiles.filter(f => matcher(f.getName))
def ends(file: File, ext: String) =
list(file, _.endsWith(ext))
def contains(file: File, query: String) =
list(file, _.contains(query))
def matches(file: File, regex: String) =
list(file, _.matches(regex))
}
柯里化
應(yīng)用「柯里化」,漂亮的「大括號(hào)」終于登上了舞臺(tái)。
object FileMatcher {
private type Matcher = String => Boolean
def list(file: File)(matcher: Matcher) =
file.listFiles.filter(f => matcher(f.getName))
def ends(file: File, ext: String) =
list(file) { _.endsWith(ext) }
def contains(file: File, query: String) =
list(file) { _.contains(query) }
def matches(file: File, regex: String) =
list(file) { _.matches(regex) }
}