重構(gòu)與函數(shù)

OO makes code understandable by encapsulating moving parting, but FP makes code understandable by minimizing moving parts. -Michael Feathers

Conditional Deferred Execution

日志Logger

if (logger.isLoggable(Level.INFO)) {
  logger.info("problem:" + getDiagnostic());
}

這個實現(xiàn)存在如下一些壞味道:

  • 重復(fù)的樣板代碼,并且散亂到用戶的各個角落;
  • logger.debug之前,首先要logger.isLoggable,logger暴露了太多的狀態(tài)邏輯,違反了LoD(Law of Demeter)

Eliminate Effects Between Unrelated Things.

Apply LoD

logger.info("problem:" + getDiagnostic());

這樣的設(shè)計雖然將狀態(tài)的查詢進行了封裝,但依然存在一個嚴(yán)重的性能問題。無論如何,getDiagnostic都將得到調(diào)用,如果它是一個耗時、昂貴的操作,將嚴(yán)重地影響了系統(tǒng)的性能。

Apply Lambda

靈活地應(yīng)用Lambda惰性求值的特性,可以很漂亮地解決這個問題。

public void log(Level level, Supplier<String> supplier) {
  if (isLoggable(level)) {
    log(supplier.get());
  }
}

public void debug(Supplier<String> supplier) {
  log(Level.DEBUG, supplier);
}

public void info(Supplier<String> supplier) {
  log(Level.INFO, supplier);
}

...

用戶的代碼也更加簡潔,省略了那些重復(fù)的樣板代碼。

logger.info(() -> "problem:" + getDiagnostic());

Apply Scala: Call by Name

在使用lambda時多余的()顯得有點冗余,可以使用by-name參數(shù)進一步提高表達力。

def log(level: Level, msg: => String) {
  if (isLoggable(level)) {
    log(msg)
  }
}

def debug(msg: => String) {
  log(DEBUG, msg)
}

def info(msg: => String) {
  log(INFO, msg)
}
logger.info("problem:" + getDiagnostic());

"problem:" + getDiagnostic()語句并非在logger.info展開計算,它被延遲計算直至被apply的時候才真正地被評估和計算。

Execute Around

我們經(jīng)常會遇到一個場景,在執(zhí)行操作之前,先準(zhǔn)備環(huán)境,之后再拆除環(huán)境。例如XUnit中的setUp/tearDown;操作數(shù)據(jù)庫時,先取得數(shù)據(jù)庫的連接,操作數(shù)據(jù)后確保釋放連接;當(dāng)操作文件時,先打開文件流,操作文件后確保關(guān)閉文件流。

Apply try-finally

為了保證異常安全性,在Java7之前,常常使用try-finally的實現(xiàn)模式解決這樣的問題。

public static String process(File file) throws IOException {
  BufferedReader bf = new BufferedReader(new FileReader(file));
  try {
    return bf.readLine();
  } finally {
    if (bf != null) 
      bf.close();
  }
}

這樣的設(shè)計和實現(xiàn)存在幾個問題:

  • if (bf != null)是必須的,但常常被人遺忘;
  • try-finally的樣板代碼遍布在用戶程序中,造成大量的重復(fù)設(shè)計;

Apply try-with-resources

Java7,只要實現(xiàn)了AutoCloseable的資源類,可以使用try-with-resources的實現(xiàn)模式,進一步簡化上例的樣板代碼。

public String process(File file) throws IOException {
  try(BufferedReader bf = new BufferedReader(new FileReader(file))) {
    return bf.readLine();
  }
}

但是,在某些場景下很難最大化地復(fù)用代碼,這使得實現(xiàn)中存在大量的重復(fù)代碼。例如遍歷文件中所有行,并替換制定模式為其他的字符串。

public String replace(File file, String regex, String i) throws IOException {
  try(BufferedReader bf = new BufferedReader(new FileReader(file))) {
    return bf.readLine().replaceAll(regex, replace);
  }
}

Apply Lambda

為了最大化地復(fù)用代碼,最小化用戶樣板代碼,將資源操作前后的代碼保持封閉,使用lambda定制與具體問題相關(guān)的處理邏輯。

process使用BufferedProcessor實現(xiàn)行為的參數(shù)化。

public static String process(File file, BufferedProcessor p) throws IOException {
  try(BufferedReader bf = new BufferedReader(new FileReader(file))) {
    return p.process(bf);
  }
}

其中,BufferedProcessor是一個函數(shù)式接口,用于描述lambda的原型信息。

@FunctionalInterface
public interface BufferedProcessor {
  String process(BufferedReader bf) throws IOException;
}

用戶使用lambda表達式,使得代碼更加簡單、漂亮。

process(file, bf -> bf.readLine());

如果使用Method Reference,可增強表達力。

process(file, BufferedReader::readLine);

Apply Scala: Structural Type, Call by Name, Currying

為了最大化地復(fù)用資源釋放的實現(xiàn),使用Scala可以神奇地構(gòu)造一個簡單的DSL,讓用戶更好地實現(xiàn)復(fù)用。

Make it Easy to Reuse.

import scala.language.reflectiveCalls

object using {
  def apply[R <: { def close(): Unit }, T](resource: => R)(f: R => T) = {
    var res: Option[R] = None
    try {
      res = Some(resource)
      f(res.get)
    } finally {
      if (res != None) res.get.close
    }
  }
}

R <: { def close(): Unit }中泛型參數(shù)R是一個擁有close方法的類型;resource: => Rresource聲明為Call by Name,可延遲計算;apply使用了兩個參數(shù),并進行了Currying化。

受益于Currying,用戶的定制的函數(shù)可以使用大括號來增強表達力,using猶如內(nèi)置的語言特性,得到抽象了的控制結(jié)構(gòu)。

using(Source.fromFile(file)) { source =>
  source.getLines 
}

因為參數(shù)source僅僅使用了一次,可以通過占位符進一步增強表達力。

using(Source.fromFile(file)) { _.getLines }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,936評論 25 709
  • Java語言的方法重載 方法的重載有3個條件1.函數(shù)位于同一類下面;2.方法名必須一樣;3.方法的參數(shù)列表不一樣;...
    CCCA閱讀 1,942評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,537評論 19 139
  • 看了《港囧》覺得挺好看,可大家都說沒有《泰囧》精彩了,可我卻覺得《港囧》講述的故事更能讓人回味,久久無法忘懷。 《...
    蘇兔閱讀 345評論 0 0
  • 間歇性躊躇滿志,持續(xù)性混吃等死。
    哎陳漂亮閱讀 117評論 0 0

友情鏈接更多精彩內(nèi)容