差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

Both sides previous revision 前次修改
下次修改
前次修改
java:java8:exception:observerexceptionhandlingoflambda [2018/01/21 23:10]
tony
java:java8:exception:observerexceptionhandlingoflambda [2023/06/25 09:48] (目前版本)
行 1: 行 1:
-====== Observer使用Lambda寫法進行通知的例外處理 ​(Working) ​======+====== Observer使用Lambda寫法通知的例外處理 ====== 
 +===== Introduction ===== 
 +這是一段常見的Observer寫法的程式碼,FileWatcher是Subject負責確認檔案是否修改,而IFileChangedListener是Observer接收檔案改變通知;當檔案改變時,FileWatcher會執行doOnChange通知各個IFileChangedListener:​ 
 +<file java FileWatcher.java>​ 
 +public class FileWatcher extends FileWatchdog {
  
 + private List<​IFileChangedListener>​ listeners = new ArrayList<>​();​
 + static private Logger logger = LoggerFactory.getLogger(FileWatcher.class);​
 +
 + public FileWatcher(String filename) {
 + super(filename);​
 + }
 +
 + @Override
 + protected void doOnChange() {
 + try {
 + if( listeners == null || listeners.isEmpty() )
 + return;
 +
 + String newContent = new String(Files.readAllBytes(Paths.get(super.filename)));​
 + listeners.forEach(listener->​listener.update(newContent));​
 + } catch( IOException e ){
 + throw new RuntimeException(e);​
 + }
 + }
 +
 + public void addListener(IFileChangedListener listener){
 + listeners.add(listener);​
 + }
 +}
 +</​file>​
 +假設我有10個listener,當第三個listener執行update時發生了例外,工作是不是就會停止了呢?​ 答案是肯定的。本篇主要分享可能的例外處理方式。
 +===== Handle Exception With A Block =====
 +最直接的方法就是把例外處理弄成一個區塊:​
 +<code java>
 +listeners.forEach(listener->​{
 + try {
 + listener.update(newContent);​
 + } catch( Exception e ) {
 + logger.warn("​Update file info failed.",​ e);
 + }
 +});
 +</​code>​
 +但隨著不同例外處理需求或流程,會變得難以閱讀而喪失原本Lambda的美意。
 +===== Extract Exception Handling As A Method =====
 +以Lambda方式實作時,將處理抽成method以增加可讀性是常使用的做法:​
 +<code java>
 +listeners.forEach(listener->​updateEx(listener,​ newContent));​
 +
 +private void updateEx(IFileChangedListener listener, String newContent){
 + try {
 + listener.update(newContent);​
 + } catch( Exception e ) {
 + logger.warn("​Update file info failed.",​ e);
 + }
 +}
 +</​code>​
 +接下來就是考慮重複使用的議題。
 +===== Wrapper The Exception Handling =====
 +為了重複使用,我們可以將處理方式透過Wrapper方式實作並集中於Utility類別:​
 +<code java>
 +public class Errors {
 + static private Logger logger = LoggerFactory.getLogger(Errors.class); ​
 + public static <T> Consumer<​T>​ logException(Consumer<​T>​ operation){
 + return i -> {
 + try {
 + operation.accept(i);​
 + } catch (Exception e) {
 + logger.warn("",​ e);
 + }
 + };
 + }
 +}
 +
 +listeners.forEach(Errors.logException(i->​i.update(newContent)));​
 +</​code>​
 +上面這種寫法只能針對unchecked exception,假如是checked exception,可以透過Functional Interface做自己的Consumer:​
 +<code java>
 + @FunctionalInterface
 + public interface ThrowingConsumer<​T,​ E extends Exception>​ {
 +     void accept(T t) throws E;
 + }
 +
 + public static <​T> ​ Consumer<​T>​ logException(ThrowingConsumer<​T,​ Exception>​ operation){
 + return i -> {
 + try {
 + operation.accept(i);​
 + } catch (Exception e) {
 + logger.warn("",​ e);
 + }
 + };
 + }
 +</​code>​
 +===== Apply durian - Error class =====
 +假如不介意相依於第三方函式庫,可以參考[[https://​github.com/​diffplug/​durian|durian]]。它針對lambda的functional interface提供了log、rethrow或者自訂義等例外處理方式,這部分之後我有時間會特別介紹。
 +===== Reference =====
 +  * [[http://​www.baeldung.com/​java-lambda-exceptions|Exceptions in Java 8 Lambda Expressions]]
 +  * [[https://​github.com/​diffplug/​durian|Github - durian]]
 +
 +====== ​ ======
 +----
 +\\
 +~~DISQUS~~