设计匠艺编程语言爱好者软件匠艺

控制抽象

2016-07-10  本文已影响733人  刘光聪

根据正交设计的基本原则,如果设计出现重复的控制逻辑,可抽象出稳定的抽象;借助于Scala强大的可扩展能力,可以将「小括号」神奇地转换为「大括号」,让用户代码感觉是一种新的控制结构。

本文通过一个简单的例子,通过若干迭代,遵循正交设计的基本原则,灵活地应用重构,逐渐改进设计,以供参考。

需求1:搜索目录下扩展名为.scala的所有文件

快速实现

object FileMatchers {
  def ends(file: File, ext: String) = {
    for (file <- file.listFiles if file.getName.endsWith(ext))
      yield filelist
  }
}

需求2:搜索目录下名字包含Test的所有文件

重复

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:搜索目录下名字正则匹配特定模式的所有文件

再现重复

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
  }
}

提取抽象

消除消除上述实现的重复,最重要的是提取公共的关注点: 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强大的类型推演能力,可以得到更为简洁的函数字面值。

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的参数由于类型修饰,显得有点过长而影响阅读;可以通过「类型别名」的机制缩短函数的类型修饰符,以便改善表达力。

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(_))
}

简化参数

简化参数传递,消除不必要的冗余,是简单设计基本原则之一。

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

可以通过定制「高阶函数」替代语法较为复杂的「for comprehension」,以便改善表达力。

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))
}

柯里化

应用「柯里化」,漂亮的「大括号」终于登上了舞台。

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) }
}
上一篇 下一篇

猜你喜欢

热点阅读