Introduction
這是一段常見的Observer寫法的程式碼,FileWatcher是Subject負責確認檔案是否修改,而IFileChangedListener是Observer接收檔案改變通知;當檔案改變時,FileWatcher會執行doOnChange通知各個IFileChangedListener:
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); } }
假設我有10個listener,當第三個listener執行update時發生了例外,工作是不是就會停止了呢? 答案是肯定的。本篇主要分享可能的例外處理方式。
Handle Exception With A Block
最直接的方法就是把例外處理弄成一個區塊:
listeners.forEach(listener->{ try { listener.update(newContent); } catch( Exception e ) { logger.warn("Update file info failed.", e); } });
但隨著不同例外處理需求或流程,會變得難以閱讀而喪失原本Lambda的美意。
Extract Exception Handling As A Method
以Lambda方式實作時,將處理抽成method以增加可讀性是常使用的做法:
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); } }
接下來就是考慮重複使用的議題。
Wrapper The Exception Handling
為了重複使用,我們可以將處理方式透過Wrapper方式實作並集中於Utility類別:
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)));
上面這種寫法只能針對unchecked exception,假如是checked exception,可以透過Functional Interface做自己的Consumer:
@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); } }; }
Apply durian - Error class
假如不介意相依於第三方函式庫,可以參考durian。它針對lambda的functional interface提供了log、rethrow或者自訂義等例外處理方式,這部分之後我有時間會特別介紹。
留言
張貼留言