跳到主要內容

Observer使用Lambda寫法對通知的例外處理

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或者自訂義等例外處理方式,這部分之後我有時間會特別介紹。

Reference

留言

這個網誌中的熱門文章

PostgreSQL - Unattended installation on windows

Introduction 要將別人軟體包裝到自己軟體中,不可或缺的東西就是Unattended installation。以Unattended installation來說,我們可以選擇透過Installer的silent mode安裝,也可以透過把目標軟體做成portable的版本。本篇文章分享這兩種方法,教導大家如何將PostgreSQL透過Unattended installation方式安裝到目標系統成為service。 Note. 本篇以PostgreSQL 10.7為例。 Install with installer Tips 安裝程式或反安裝程式的參數,除了可以直接上官網搜尋Installation User Guide以外,也可以直接使用help參數查詢: postgresql- 10.7 - 2 -windows-x64.exe --help Windows安裝程式主要有EnterpriseDB與BigSQL兩種。BigSQL版本安裝元件是透過網路下載且支援參數不如EnterpriseDB版本多,以我們需求來說,我們傾向於使用EnterpriseDB版本。接下來分享給大家安裝與反安裝方法。 Installation @ echo off set INSTALL_DIR =C:\postgres10 set INSTALLER =postgresql- 10.7 - 2 -windows-x64.exe   rem options for installation set SSMDB_SERVICE =postgresql- 10 set MODE =--unattendedmodeui none --mode unattended   set DB_PASSWD =--superpassword postgres set DB_PORT =--serverport 5432   set SERVICE_NAME =--servicename % SSMDB_SERVICE %   set PREFIX =--prefix "%INSTALL_DIR%" set DATA_DIR =--datadir "%INSTALL_DIR%\data"   set OPTIONS =

How to install RIDE on Windows?

Introduction 多年沒在Windows上開發RobotFramework,趁著這次整理一下RIDE安裝方法。 目前RIDE最新版本與Python對應版本如下: (3.6 < python <= 3.11) Install current released version (2.0.8.1) with: pip install -U robotframework-ride 安裝Python 直接到Python官網找尋最新的3.11版本,我使用3.11.9: link 。安裝就是一直下一步而已。 安裝wxPython 每次安裝RIDE最困難的都是wxPython。看了一下 官網 描述,我就姑且相信一下: 接著進入下 載頁面 就有安裝教學。基本上就是到Python目錄下的Scripts直接執行以下command: pip install -U wxPython 安裝RIDE 接著就如RIDE官網所說,執行以下command: pip install -U robotframework-ride 啟動RIDE 直接在相同目錄下執行ride就可以啟動了,你也可以直接在桌面建ride連結,加快下次啟動時間。 沒想到這次這麼順利就安裝完成了。因為我是使用java去啟動robot framework,就不特別講要怎麼使用pip安裝robot framework了。

Hello World!

即將要搬家,因此舊網頁內容將慢慢轉移至Blogger。 如果要存取舊網頁,可以使用以下連結: https://wiki.tonylin.idv.tw/dokuwiki/doku.php