1. 程式人生 > >設計模式之裝飾器模式(java實現)

設計模式之裝飾器模式(java實現)

裝飾器模式(Decorator):結構型設計模式,為了實現類在不修改原始類的基礎上進行動態的覆蓋或者增加方法,該實現保持了跟原有類的層級關係。這種設計模式允許向一個現有的物件新增新的功能,同時又不改變其結構。算是一種非常特殊的介面卡模式。

在實際業務中,有時候我們會建立了多層子類,但如果當子類層數超過三層,一般來說不太建議,這個時候可以考慮使用裝飾器模式。

Spring中的應用場景:在我們的專案中遇到這樣一個問題:我們的專案需要連線多個數據庫,而且不同的客戶在每 次訪問中根據需要會去訪問不同的資料庫。我們以往在 Spring 和 Hibernate 框架中總是配置一個數據 源,因而 SessionFactory 的 DataSource 屬性總是指向這個資料來源並且恆定不變,所有 DAO 在使用SessionFactory 的時候都是通過這個資料來源訪問資料庫。但是現在,由於專案的需要,我們的 DAO 在 訪問 SessionFactory 的時候都不得不在多個數據源中不斷切換,問題就出現了:如何讓SessionFactory 在執行資料持久化的時候,根據客戶的需求能夠動態切換不同的資料來源?我們能不能 在 Spring 的框架下通過少量修改得到解決?是否有什麼設計模式可以利用呢?
首先想到在 Spring 的 ApplicationContext 中配置所有的 DataSource。這些 DataSource 可能是各 種不同型別的,比如不同的資料庫:Oracle、SQL Server、MySQL 等,也可能是不同的資料來源:比如Apache 提 供 的 org.apache.commons.dbcp.BasicDataSource 、 Spring 提 供 的org.springframework.jndi.JndiObjectFactoryBean 等。然後 SessionFactory 根據客戶的每次 請求,將 DataSource 屬性設定成不同的資料來源,以到達切換資料來源的目的。


由於裝飾器模式算是一種非常特殊的介面卡模式,所以,我這邊使用之前介面卡模式中的那個例子進行改造,使用裝飾器模式來實現。之前介面卡模式的java例子

依舊是新增登入類別,不過此時不同的是新增兩個介面ISignInService,ISignInForThirdService.

ISignInForThirdService是繼承於ISignInService的,SignInForThirdService不再是對SignInService的繼承,而是對ISignInForThirdService的實現

類圖

具體實現:

User類:

package Decorator;

public class User {
    private String username;
    private String password;
    private String mid;
    private String info;

    public User() {
    }

    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;
    }

    public String getMid() {
        return mid;
    }

    public void setMid(String mid) {
        this.mid = mid;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

 ResultMsg:

package Decorator;

public class ResultMsg {
    private int code;
    private String msg;
    private Object data;

    public ResultMsg( int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

ISignInService介面和SignInService類:

package Decorator;

public interface ISignInService {
    /**
     * 註冊介面
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password);


    /**
     * 登入的介面
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password);
}
package Decorator;

public class SignInService implements ISignInService {
    /**
     * 註冊方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password){
        return  new ResultMsg(200,"註冊成功",new User());
    }


    /**
     * 登入的方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password){
        System.out.println("登陸成功");
        return null;
    }
}

 ISignInForThirdService介面和SignInForThirdService類:

package Decorator;

public interface ISignInForThirdService extends ISignInService {
    public ResultMsg loginForQQ(String openId);

    public ResultMsg loginForWechat(String openId);

    public ResultMsg loginForToken(String token);

    public ResultMsg loginForTelephone(String telephone, String code);

    public ResultMsg loginForRegister(String username, String password);

    public ResultMsg login(String username, String password);
}

 

package Decorator;

public class SignInForThirdService implements ISignInForThirdService {

    private ISignInService service;
    public SignInForThirdService(ISignInService service){
        this.service = service;
    }

    @Override
    public ResultMsg register(String username, String password) {
        return service.register(username,password);
    }

    @Override
    public ResultMsg login(String username, String password) {
        return service.login(username,password);
    }


    public ResultMsg loginForQQ(String openId){
        //1、openId是全域性唯一,我們可以把它當做是一個使用者名稱(加長)
        //2、密碼預設為QQ_EMPTY
        //3、註冊(在原有系統裡面建立一個使用者)

        //4、呼叫原來的登入方法
        String QQDefaultPasswords = "QQ_EMPTY";
        //這裡省略查重驗證,預設為新使用者,實際專案執行會有

        System.out.println("QQ登入");

        return loginForRegister(openId,QQDefaultPasswords);
    }

    public ResultMsg loginForWechat(String openId){
        String WechatDefaultPasswords = "WECHAT_EMPTY";
        System.out.println("wechat登入");
        return loginForRegister(openId,WechatDefaultPasswords);

    }

    public ResultMsg loginForToken(String token){
        //通過token拿到使用者資訊,然後再重新登陸了一次
        User user = new User();
        System.out.println("token自動登入");
        return login(user.getUsername(),user.getPassword());
    }

    public ResultMsg loginForTelephone(String telephone, String code){

        String telephoneDefaultPasswords = "TELEPHONE_EMPTY";
        System.out.println("手機號登入");
        return loginForRegister(telephone,telephoneDefaultPasswords);
    }

    public ResultMsg loginForRegister(String username, String password){
        this.register(username,password);
        return this.login(username,password);
    }

}

比較特殊的是實際的呼叫方式的實現SignInForThirdServiceTest:

package Decorator;

public class SignInForThirdServiceTest {
    public static void main(String[] args) {

        ISignInForThirdService iSignInForThirdService = new SignInForThirdService(new SignInService());

        //原來的功能依舊對外開放,依舊保留
        //新的功能同樣的也可以使用
        iSignInForThirdService.loginForQQ("sdfgdgfwresdf9123sdf");
        iSignInForThirdService.loginForTelephone("1560017471","sdha");
        iSignInForThirdService.loginForToken("dsajdsakldjksafjhfkasljkla");
        iSignInForThirdService.loginForWechat("dhafkahkjdsada");

    }
}

使用SignInForThirdService來裝飾SignInService;

執行結果:

以上就是一個裝飾器模式的實現了。

可以看到裝飾器模式可以替代繼承,來實現功能。因此,但我們要實現一個類的功能擴充套件的時候,可以考慮裝飾器模式。

那麼裝飾器模式和介面卡模式的差別是哪些呢?

裝飾器模式 介面卡模式
是一種非常特別的介面卡模式  可以不保留層級關係

裝飾者和被裝飾者都要實現同一個介面,

主要目的是為了擴充套件,依舊保留OOP關係 

適配者和被適配者沒有必然的層級聯絡

通常採用代理或者繼承形式進行包裝

 

滿足is-a的關係   滿足has-a關係
注重的是覆蓋、擴充套件   注重相容、轉換

在Spring 原始碼中,但類名裡面有 Wrapper,Decorator,基本上可以認為是裝飾器模式。