設計模式講解與代碼實踐(十)——裝飾

本文來自李明子csdn博客(http://blog.csdn.net/free1985),商業轉載請聯繫博主獲得授權,非商業轉載請註明出處!

1 目的

裝飾(Decorator)提供了一種動態爲對象添加額外能力的方法。

2 基本形態

裝飾的基本形態如類圖2-1所示。
圖2-1  裝飾類圖
圖2-1 裝飾類圖

3 參與者

結合圖2-1,下面介紹各類在裝飾設計模式中扮演的角色。
3.1 Component
Component是業務類接口,聲明瞭業務類需要實現的各方法。
3.2 ConcreteComponent
ConcreteComponent是具體的業務類,實現了Component接口。在裝飾模式中,ConcreteComponent是實際被動態裝飾的對象的類。
3.3 Decorator
Decorator是裝飾者。Decorator實現了Component接口,以使它可以像被裝飾對象一樣使用。Decorator包含一個Component類型的成員變量,該對象在構造方法中初始化,是被裝飾的對象。Decorator在實現Component接口聲明的各方法時調用被裝飾對象的對應方法。
通常來講,Decorator被聲明爲抽象類。但如果只有一個具體裝飾者,則無需添加這個抽象層。
3.4 ConcreteDecorator
ConcreteDecoratorA和ConcreteDecoratorB是具體裝飾者。它們從Decorator抽象類派生,實現Component接口。
它們通過重寫業務方法的形式實現對被裝飾類的“擴展”。裝飾模式的“擴展”方式主要是指對原有業務方法的執行前後的附加操作,因此一般來說,重寫的方法將首先調用裝飾類自身的前置方法,再調用從Decorator繼承的對應方法(實際是調用了被裝飾者的方法),最後調用自身的後置方法。
3.5 Client
Client是裝飾設計模式的使用者,在圖2-1中並未繪製。

4 代碼實踐

下面我們用一個業務場景實例來進一步講解裝飾的使用。
4.1 場景介紹
某用戶控制器可以輸出指定id的用戶的信息。可以在業務系統中配置用戶信息的緩存,有H2和Redis兩種緩存可供選擇。緩存不是必須的。
以下各節將介紹該場景各類的具體實現及其在裝飾設計模式中所對應的參與者角色。
4.2 IUserMgmt
IUserMgmt是用戶管理接口,聲明瞭“獲取用戶名”接口getUserName。對應於裝飾模式的參與者,IUserMgmt是業務類接口Component。下面的代碼給出了IUserMgmt的聲明。

package demo.designpattern.decorator;

/**
 * 用戶管理接口
 * Created by LiMingzi on 2017/7/20.
 */
public interface IUserMgmt {
    /**
     * 獲取用戶名
     * @param id 用戶id
     * @return 用戶名
     */
    String getUserName(String id);
}

4.3 UserMgmt
UserMgmt是用戶管理類,實現了IUserMgmt接口。對應於裝飾模式的參與者,UserMgmt是具體業務類ConcreteComponent。下面的代碼給出了UserMgmt的聲明。

package demo.designpattern.decorator;

/**
 * 用戶管理類
 * Created by LiMingzi on 2017/7/20.
 */
public class UserMgmt implements IUserMgmt{
    /**
     * 獲取用戶名(demo)
     *
     * @param id 用戶id
     * @return 用戶名
     */
    @Override
    public String getUserName(String id) {
        System.out.println("debug--從數據庫獲取id爲'"+id+"'的用戶名");
        if(id.equals("001")){
            return "張三";
        }else{
            return "李四";
        }
    }
}

上述代碼中,15行實現了接口getUserName。該方法是演示方法,實現時只是根據id枚舉獲取用戶名,模擬從關係數據庫中查詢。另外,16行的調試輸出可以讓我們瞭解用戶名的實際獲取渠道。
4.4 UserMgmtDecorator
UserMgmtDecorator是用戶管理類裝飾者,實現了IUserMgmt接口。對應於裝飾模式的參與者,UserMgmtDecorator是裝飾者Decorator。下面的代碼給出了UserMgmtDecorator的聲明。

package demo.designpattern.decorator;

/**
 * 用戶管理類裝飾者
 * Created by LiMingzi on 2017/7/20.
 */
public abstract class UserMgmtDecorator implements IUserMgmt{
    /**
     * 用戶管理接口對象
     */
    private IUserMgmt userMgmt;

    /**
     * 構造方法
     * @param userMgmt 用戶管理對象
     */
    public UserMgmtDecorator(IUserMgmt userMgmt) {
        this.userMgmt = userMgmt;
    }

    /**
     * 獲取用戶名
     *
     * @param id 用戶id
     * @return 用戶名
     */
    @Override
    public String getUserName(String id) {
        return userMgmt.getUserName(id);
    }
}

上述代碼中,7行,UserMgmtDecorator被聲明爲抽象類,因爲我們實際上有兩個具體裝飾者UserMgmtBaseH2Cache和UserMgmtBaseRedisCache。11行,聲明瞭用戶管理接口類型的成員變量userMgmt,該變量在17行的構造方法中接受被裝飾者進而初始化。28行,實現getUserName接口時,直接調用了被裝飾對象的同名方法。
4.5 UserMgmtBaseH2Cache
UserMgmtBaseH2Cache是基於H2緩存的用戶管理類,從UserMgmtDecorator派生,實現了IUserMgmt接口。對應於裝飾模式的參與者,UserMgmtBaseH2Cache是具體裝飾者ConcreteDecorator。下面的代碼給出了UserMgmtBaseH2Cache的聲明。

package demo.designpattern.decorator;

import java.util.HashMap;
import java.util.Map;

/**
 * 基於H2緩存的用戶管理類
 * Created by LiMingzi on 2017/7/20.
 */
public class UserMgmtBaseH2Cache extends UserMgmtDecorator implements IUserMgmt {
    /**
     * 用戶字典,key爲用戶id,value爲用戶名
     */
    private static Map<String,String> userMap=new HashMap<>();
    /**
     * 構造方法
     *
     * @param userMgmt 用戶管理對象
     */
    public UserMgmtBaseH2Cache(IUserMgmt userMgmt) {
        super(userMgmt);
    }

    /**
     * 獲取用戶名
     *
     * @param id 用戶id
     * @return 用戶名
     */
    @Override
    public String getUserName(String id) {
        // 用戶名
        String name = getUserNameFromH2(id);
        if(name==null){
            name = super.getUserName(id);
            setUserH2(id,name);
        }
        return name;
    }

    /**
     * 從H2緩存獲取用戶名(demo)
     * @param id 用戶id
     * @return 用戶名
     */
    private String getUserNameFromH2(String id){
        if(userMap.containsKey(id)){
            System.out.println("debug--從H2緩存獲取id爲'"+id+"'的用戶名");
            return userMap.get(id);
        }
        return null;
    }

    /**
     * 將用戶信息保存至H2緩存(demo)
     * @param id 用戶id
     * @param name 用戶名
     */
    private void setUserH2(String id,String name){
        userMap.put(id,name);
    }
}

上述代碼中,46行新增了“從H2緩存獲取用戶名”方法getUserNameFromH2,該方法從H2中查詢指定用戶的用戶名。59行,新增了“將用戶信息保存至H2緩存”方法setUserH2,將用戶信息緩存至H2。31行,重寫了“獲取用戶名”方法getUserName。33行,調用getUserNameFromH2方法從H2查詢用戶名。如果沒有查詢到,則調用被裝飾者方法(35行)從關係數據庫查詢用戶名,並將結果緩存至H2(36行)。
4.6 UserMgmtBaseRedisCache
UserMgmtBaseRedisCache是基於Redis緩存的用戶管理類,從UserMgmtDecorator派生,實現了IUserMgmt接口。對應於裝飾模式的參與者,UserMgmtBaseRedisCache是具體裝飾者ConcreteDecorator。下面的代碼給出了UserMgmtBaseRedisCache的聲明。

package demo.designpattern.decorator;

import java.util.HashMap;
import java.util.Map;

/**
 * 基於Redis緩存的用戶管理類
 * Created by LiMingzi on 2017/7/20.
 */
public class UserMgmtBaseRedisCache  extends UserMgmtDecorator implements IUserMgmt  {
    /**
     * 用戶字典,key爲用戶id,value爲用戶名
     */
    private static Map<String,String> userMap=new HashMap<>();
    /**
     * 構造方法
     *
     * @param userMgmt 用戶管理對象
     */
    public UserMgmtBaseRedisCache(IUserMgmt userMgmt) {
        super(userMgmt);
    }

    /**
     * 獲取用戶名
     *
     * @param id 用戶id
     * @return 用戶名
     */
    @Override
    public String getUserName(String id) {
        // 用戶名
        String name = getUserNameFromRedis(id);
        if(name==null){
            name = super.getUserName(id);
            setUserRedis(id,name);
        }
        return name;
    }

    /**
     * 從Redis緩存獲取用戶名(demo)
     * @param id 用戶id
     * @return 用戶名
     */
    private String getUserNameFromRedis(String id){
        if(userMap.containsKey(id)){
            System.out.println("debug--從Redis緩存獲取id爲'"+id+"'的用戶名");
            return userMap.get(id);
        }
        return null;
    }

    /**
     * 將用戶信息保存至Redis緩存(demo)
     * @param id 用戶id
     * @param name 用戶名
     */
    private void setUserRedis(String id,String name){
        userMap.put(id,name);
    }
}

上述代碼中,46行新增了“從Redis緩存獲取用戶名”方法getUserNameFromRedis,該方法從Redis中查詢指定用戶的用戶名。59行,新增了“將用戶信息保存至Redis緩存”方法setUserRedis,將用戶信息緩存至Redis。31行,重寫了“獲取用戶名”方法getUserName。33行,調用getUserNameFromRedis方法從Redis查詢用戶名。如果沒有查詢到,則調用被裝飾者方法(35行)從關係數據庫查詢用戶名,並將結果緩存至Redis(36行)。
4.7 UserController
UserController類是用戶控制器類,對應於裝飾模式的參與者,UserController是客戶Client。下面的代碼給出了UserController的聲明。

package demo.designpattern.decorator;

/**
 * 用戶控制器
 * Created by LiMingzi on 2017/7/20.
 */
public class UserController {
    /**
     * 獲取緩存類型,demo
     * @return 緩存類型
     */
    public String getCacheType() {
        return cacheType;
    }

    /**
     * 設置緩存類型,demo
     * @param cacheType 緩存類型
     */
    public void setCacheType(String cacheType) {
        this.cacheType = cacheType;
    }

    /**
     * 緩存類型
     */
    private String cacheType="";
    /**
     * 輸出用戶信息
     * @param id 用戶id
     */
    public void outputUserInfo(String id){
        // 用戶管理接口對象
        IUserMgmt userMgmt = getUserMgmt();
        if(getCacheType().equals("H2")){
            userMgmt = new UserMgmtBaseH2Cache(userMgmt);
        }else if(getCacheType().equals("Redis")){
            userMgmt = new UserMgmtBaseRedisCache(userMgmt);
        }
        System.out.println(userMgmt.getUserName(id));
    }

    /**
     * 獲取用戶管理對象(demo)
     * @return
     */
    private IUserMgmt getUserMgmt(){
        return new UserMgmt();
    }
}

上述代碼中,32行,輸出用戶信息方法outputUserInfo輸出指定id的用戶名。34行,調用getUserMgmt方法實例化用戶管理接口對象。在實際場景中,該對象的構建過程可能很複雜,也可能不在當前類中。35-39行,通過判斷當前使用的緩存類型實例化具體裝飾者。爲了便於演示,在此提供了設置緩存類型的方法setCacheType(21行),在實際場景中該配置應該持久化於配置文件或關係數據庫中。
4.8 測試代碼
爲了測試本文中的代碼,我們可以編寫如下測試代碼。其中,第一組使用H2緩存,第二組使用Redis緩存,最後一組不使用緩存。

    /**
     * 裝飾測試
     */
    public static void decoratorTest(){
        // 用戶控制器
        demo.designpattern.decorator.UserController userController = new demo.designpattern.decorator.UserController();
        // 使用H2緩存,demo
        userController.setCacheType("H2");
        userController.outputUserInfo("001");
        userController.outputUserInfo("001");
        System.out.println("----------------------------------------------");
        // 使用Redis緩存,demo
        userController.setCacheType("Redis");
        userController.outputUserInfo("001");
        userController.outputUserInfo("001");
        System.out.println("----------------------------------------------");
        // 使用緩存,demo
        userController.setCacheType("");
        userController.outputUserInfo("001");
        userController.outputUserInfo("001");
    }

編譯運行後,得到如下測試結果:
debug–從數據庫獲取id爲’001’的用戶名
張三
debug–從H2緩存獲取id爲’001’的用戶名
張三

—————————————————————
debug–從數據庫獲取id爲’001’的用戶名
張三
debug–從Redis緩存獲取id爲’001’的用戶名
張三

—————————————————————
debug–從數據庫獲取id爲’001’的用戶名
張三
debug–從數據庫獲取id爲’001’的用戶名
張三

發佈了56 篇原創文章 · 獲贊 28 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章