用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();
}