1. 程式人生 > >用java觀察者模式解耦經典三層架構

用java觀察者模式解耦經典三層架構

三層架構是一個非常經典的架構模式,根據系統的職責不同,將系統分成了表現層,邏輯層和資料訪問層,並且配合資料實體進行資料傳輸,可以大大的封裝性和複用性。

經典的三層架構圖:


我們再深入到架構圖內部,看看具體的類圖,用簡單的登陸舉例吧:


這裡通過LoginUI,呼叫了LoginLogService和LoginVerificationService兩個類,通過類圖可以看得出,U層和Service層(本文把BLL層稱為了Service層)直接採用了直接呼叫的方式。在面向物件的設計中,一直強調要面向介面程式設計,好我們把介面加上:


好了,加上介面以後,再用上工廠方法或者依賴管理的框架spring,就可以實現U層和Service層的解耦了,我們隨時針對LoginLogService和LoginVerificationService進行替換,這既符合針對介面程式設計,由符合開閉原則,也符合里氏替換。

好,那我有一個問題,我不想更改當前的兩個邏輯,想新增第三個邏輯。是不是得改U層的類了?

好了,接下來是觀察者模式應該出場的時候了,讓我們看看怎麼利用這個模式來解決邏輯新增,來實現三層架構的真正解耦(這裡主要指U層和Service層的耦合)。其實觀察者模式就是java委託的實現方式。

首先說明下思路,我們把LoginUI作為事件源,發使用者名稱密碼作為訊息傳送出去,註冊過的Service可以處理訊息,也就是說通過訊息對U層和Service層進行了解耦。來看下類圖


這裡LoginUI變成了LoginEventSource,變成了事件源,並且繼承了EventSource抽象類,兩個Service類繼承了Listener介面,成為了監聽者。那具體是怎麼解耦的呢?

首先看EventSource抽象類,該抽象類實現了事件源的註冊、刪除和通知方法。

package com.tgb.chargeSystem;

import java.util.ArrayList;

public abstract class EventSource {
	//keep registered listener 
	private ArrayList<Listener> listeners=new ArrayList<Listener>();
	
	//register listener to EventSource
	//EventSource have data that Listener interested in
	public void registerListener(Listener listener){
		listeners.add(listener);
	}
	
	//stop focuse on this event
	public void removeListener(Listener listener){
		int i=listeners.indexOf(listener);
		if(i>0){
			listeners.remove(i);
		}
	}
	
	//EventSource notify listener and send itself as message 
	public void notifyListenner(){
		for(int i=0;i<listeners.size();i++){
			Listener listener=listeners.get(i);
			listener.actionPerformed(this);
		}
	}
	
}


接下來看下EventSource的子類LoginEventSource,它提供了兩個回撥方法。

package com.tgb.chargeSystem;

import java.util.ArrayList;

public class LoginEventSource extends EventSource{
	private String username="xqf";
	private String password="123";
	
	//Callback method ,when success in verification,be invoked 
	public String LoginSuccess(){
		System.out.println(this.getClass().getName()+"print:Login Success");
		return "Success";
	}
	//Callback method ,when fail in verification,be invoked 
	public String LoginFail(){
		System.out.println(this.getClass().getName()+"print:Login Fail");
		return "Fail";
	}
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

接下來看看Listener介面
package com.tgb.chargeSystem;

public interface Listener {
	void actionPerformed(EventSource e);
}

實現了Listener介面的LoginLogListener類
package com.tgb.chargeSystem;

public class LoginLogListener implements Listener {
	
	public LoginLogListener(EventSource eventSource){
		eventSource.registerListener(this);
	}
	@Override
	public void actionPerformed(EventSource e) {
		
		System.out.println("======儲存日誌到txt檔案==========");
		System.out.println("username:"+((LoginEventSource)e).getUsername()+",password:"+((LoginEventSource)e).getPassword());
		System.out.println("=======儲存完畢==========");
		
	}

}
實現了Listener介面的LoginVerificationListener類:
package com.tgb.chargeSystem;

public class LoginVerificationListener implements Listener{
	
	private static final String USERNAME="xqf";
	private static final String PASSWORD="123"; 
	
	public LoginVerificationListener(EventSource eventSource){
		//register itself to EventSource
		eventSource.registerListener(this);
	}
	@Override
	public void actionPerformed(EventSource e) {
		System.out.println(this.getClass().getName()+"print: invoke actionPerformed method");
		LoginEventSource les=(LoginEventSource)e;
		//according to username and passowrd,call Callback method
		if(les.getUsername().equals(USERNAME)&&les.getPassword().equals(PASSWORD)){
			les.LoginSuccess();
		}else{
			les.LoginFail();
		}
	}
	
}

到此,我們還需要一個類,把listener類註冊到事件源,這樣,事件源也就是U層就可以發訊息給Service層了,這裡我們暫且把這個類放到U層。這是當我們再想新增邏輯,只需要實現Listener介面,並且修改一下下面這個類,把新新增的類註冊到LoginEventSource上就可以了。
public static void main(String[] args){
		
		
		LoginEventSource loginEventSource=new LoginEventSource();
		
		LoginLogListener lll=new LoginLogListener(loginEventSource);
		LoginVerificationListener lvl=new LoginVerificationListener(loginEventSource);
		
		loginEventSource.notifyListenner();
}