1. 程式人生 > >從原始碼的角度理解Java設計模式的裝飾模式

從原始碼的角度理解Java設計模式的裝飾模式

一、裝飾模式介紹

修飾符模式定義:不改變原始物件的附加函式比生成子類更靈活。

適用場景:動態的給一個物件新增或者撤銷功能。

優點:它能夠在不改變原有物件的情況下動態擴充套件函式,使擴充套件函式按照期望的順序執行,達到不同的效果。

缺點:更多的類,使程式複雜

型別:結構型。

類圖:

原始碼分析中的典型應用

  • Java I/O 中的裝飾者模式
  • Spring Session 中的裝飾者模式
  • Mybatis 快取中的裝飾者模式

二、給系統新增日誌,安全、限流示例

可以抽取出通用系統的安全性、日誌、當前限制等獨立於業務的程式碼,在控制器轉換前後使用模板法模式可以部分解決上述問題。

public abstract class BaseAspect {
    Logger logger = LoggerFactory.getLogger(BaseCommand.class);
    public void execute(){
	    //記錄日誌
        logger.debug("..start..");
       //過濾跨站指令碼攻擊
       paramXssAspect();
        //限制速率
        doRateLimit();

        doBusiness();

        logger.debug("..end..");
    }
    public abstract void doBusiness();

}
class PlaceOrderAspect extends BaseAspect {
    @Override
    public void doBusiness() {
        //下單操作
    }
}
class PayOrderAspect extends BaseAspect {
    @Override
    public void doBusiness() {
        //支付操作
    }
}

在父類中,已經編寫了“雜亂”的非業務程式碼,只剩下一個抽象的方法和其他子類來實現,子類變得非常清新,只關注業務邏輯。
這種方法的最大缺點是父類定義了一切:為了執行那些非業務程式碼,按照什麼順序等等,子類只能被無條件地接受。如果有一個子類不限制速率,那麼它就無法擺脫它。

通過使用裝飾器模型,我們可以靈活地處理上述問題。

//最高層抽象元件
interface IAspect {
    String doHandlerAspect();
}

//基本被裝飾類,做一些公共處理
class AspectImpl implements IAspect{

    @Override
    public String doHandlerAspect() {
        return "裸跑程式碼.";
    }
}

abstract class AbstractDecorator implements IAspect{
    //很重要,組合抽象構件到自己的類中
    private IAspect aspect;

    public AbstractDecorator(IAspect aspect) {//通過IAspect構造自己
        this.aspect = aspect;
    }
    @Override
    public String doHandlerAspect() {
        return this.aspect.doHandlerAspect();
    }
}

附加記錄日誌,安全,限流功能:

class LoggerAspect extends  AbstractDecorator{
    public LoggerAspect(IAspect aspect){
        super(aspect);
    }
    @Override
    public String doHandlerAspect() {
        return super.doHandlerAspect()+"+記錄日誌.";
    }
}
class ParamXssAspect extends  AbstractDecorator{
    public ParamXssAspect(IAspect aspect){
        super(aspect);
    }
    @Override
    public String doHandlerAspect() {
        return super.doHandlerAspect()+"+過濾危險字元.";
    }
}
class LimitAspect extends  AbstractDecorator{
    public LimitAspect(IAspect aspect){
        super(aspect);
    }
    @Override
    public String doHandlerAspect() {
        return super.doHandlerAspect()+"+限流.";
    }
}

測試一下:

public class Test {
    public static void main(String[] args) {
        IAspect aspect = new LimitAspect(new ParamXssAspect(new LoggerAspect(new AspectImpl())));
        System.out.println(aspect.doHandlerAspect());
    }
}

執行結果:

------

裸跑程式碼.+記錄日誌.+過濾危險字元.+限流.

------

從上面可以看出,裝飾器模式可以按任意順序組裝函式,是否非常靈活?此外,上述三個函式還可以封裝到註釋@Log、@ParamXss、@AccessLimit中以實現可插拔性。

三、原始碼中的裝飾者模式

3.1、Java IO中是體現最明顯的裝飾者模式。

它是.stream(InputStream/OutputStream),bytestream(Reader/Writer)是類,InputStream中的輸入,ReaderClass:

這裡總結幾種常用流的應用場景:

 

3.2、Spring Session中的ServletRequestWrapper(Response也一樣)的裝飾者模式。

public class ServletRequestWrapper implements ServletRequest {
    private ServletRequest request;//組合抽象介面到自己的類中

    public ServletRequestWrapper(ServletRequest request) {//可以構造自己
        if(request == null) {
            throw new IllegalArgumentException("Request cannot be null");
        } else {
            this.request = request;
        }
    }

    public ServletRequest getRequest() {
        return this.request;
    }

    public void setRequest(ServletRequest request) {
        if(request == null) {
            throw new IllegalArgumentException("Request cannot be null");
        } else {
            this.request = request;
        }
    }
   //省略...
}

3.3、Spring Cache中的TransactionAwareCacheDecorator的裝飾者模式。

其實從類名就可以看出。

public class TransactionAwareCacheDecorator implements Cache {
    private final Cache targetCache;//把Cache組合到自己類中

    public TransactionAwareCacheDecorator(Cache targetCache) {//通過Cache構造自己
        Assert.notNull(targetCache, "Target Cache must not be null");
        this.targetCache = targetCache;
    }

    public <T> T get(Object key, Class<T> type) {
        return this.targetCache.get(key, type);
    }

    public void put(final Object key, final Object value) {
        // 判斷是否開啟了事務
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            // 將操作註冊到 afterCommit 階段
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                public void afterCommit() {
                    TransactionAwareCacheDecorator.this.targetCache.put(key, value);
                }
            });
        } else {
            this.targetCache.put(key, value);
        }
    }
    // ...省略...
}

3.4、Mybatis中的裝飾者。

Cache是一個抽象的元件類,PerpetualCache是一個具體的元件類,decorators包下的類是裝飾類。沒有抽象裝飾類。