最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java設(shè)計(jì)模式之裝飾器模式

 更新時(shí)間:2022年10月11日 09:08:04   作者:tianClassmate  
這篇文章介紹了Java設(shè)計(jì)模式之裝飾器模式,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

本文由老王將建好的書房計(jì)劃請(qǐng)小王來幫忙,小王卻想謀權(quán)篡位,老王通過教育他引出裝飾器設(shè)計(jì)模式,第二部分針對(duì)老王提出的建設(shè)性意見實(shí)現(xiàn)裝飾器模式,第三部分針對(duì)裝飾器模式在Jdk中的IO、Spring中的緩存管理器、Mybatis的運(yùn)用來加強(qiáng)我們的理解,第四部分說明裝飾器模式和代理模式的區(qū)別及他們各自的應(yīng)用場(chǎng)景。

讀者可以拉取完整代碼到本地進(jìn)行學(xué)習(xí),實(shí)現(xiàn)代碼均測(cè)試通過后上傳到碼云,本地源碼下載。

一、引出問題

上篇文章(Java設(shè)計(jì)模式之組合模式)對(duì)老王的書架改造以后,老王是相當(dāng)?shù)臐M意,看小王能力突出,這不老王又有了新的需求。

經(jīng)過組合模式以后老王的書被管理的井井有條,但是隨著書的增多,老王就有一些忙不過來了,老王就想讓小王幫他處理一些額外的事,比如在買書之前打掃一下書房,在晚上的時(shí)候把書房的門鎖一下;或者有人借書之前做一下記錄,借書者還書以后小王接收一下,等等。

小王聽完說這有何難,說完擼起袖子就準(zhǔn)備改老王的代碼。老王急忙攔住了他,你真是個(gè)呆瓜,我寫的代碼你憑什么要?jiǎng)樱愀牧藭?huì)不會(huì)影響我的業(yè)務(wù)邏輯,平時(shí)讓你多看書你不聽,之前學(xué)的設(shè)計(jì)模式呢?不拿出來用,眼看著讓他吃灰。

小王不好意思的撓撓頭,翻出來了他的設(shè)計(jì)模式寶典,開始尋找合適的設(shè)計(jì)模式。

小王大喊有了,之前說過的代理模式可以很好的解決這個(gè)問題,代理模式可以動(dòng)態(tài)的增強(qiáng)對(duì)象的一些特性,我準(zhǔn)備使用代理模式完成這個(gè)需求。

老王聽完止不住的搖搖頭,看來你是打算謀權(quán)篡位了,你是想要我整個(gè)書房的權(quán)利呀!

老王解釋說,代理模式是可以實(shí)現(xiàn)這個(gè)需求,但是在這個(gè)場(chǎng)景下顯然代理模式不合適,代理模式是著重對(duì)對(duì)象的控制,而我們今天的需求是在該對(duì)象的基礎(chǔ)之上增加他的一些功能,我們各自的業(yè)務(wù)獨(dú)立發(fā)展互不干擾。

二、裝飾器模式概念與使用

實(shí)際上,在原對(duì)象的基礎(chǔ)之上增加其功能就是屬于裝飾器模式。

裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它是作為現(xiàn)有的類的一個(gè)包裝。

在裝飾器模式中應(yīng)該是有四個(gè)角色:

①Component抽象構(gòu)件(老王抽象方法)
②ConcreteComponent 具體構(gòu)件(老王實(shí)現(xiàn)方法)
③Decorator裝飾角色(裝飾者小王)
④ConcreteDecorator 具體裝飾角色(裝飾者小王實(shí)現(xiàn)方法)

在裝飾器模式中,需要增強(qiáng)的類(被裝飾者)要實(shí)現(xiàn)接口,裝飾者繼承被裝飾者的接口,并將被裝飾者的實(shí)例傳進(jìn)去,在具體裝飾角色中調(diào)用被裝飾者的方法,在其前后定義增強(qiáng)的方法,在實(shí)際應(yīng)用中往往裝飾角色和具體裝飾角色合二為一。

我們看下具體的代碼實(shí)現(xiàn):

抽象構(gòu)件:

/**
 * 書的抽象構(gòu)件
 * @author tcy
 * @Date 10-08-2022
 */
public abstract class ComponentBook {

    /**
     * 借書
     */
    public abstract void borrowBook();

    /**
     * 買書
     */
    public abstract void buyBook();

}

書的具體構(gòu)件:

/**
 * 書的具體構(gòu)件
 * @author tcy
 * @Date 10-08-2022
 */
public class ConcreteComponentBook extends ComponentBook{
    @Override
    public void borrowBook() {
        System.out.println("老王的書借出去...");

    }

    @Override
    public void buyBook() {
        System.out.println("老王的書買回來...");

    }
}

裝飾角色:

/**
 * 書的裝飾者
 * @author tcy
 * @Date 10-08-2022
 */
public class DecoratorBook extends ComponentBook{

    private ComponentBook componentBook;

    DecoratorBook(ComponentBook componentBook){
        this.componentBook=componentBook;
    }

    @Override
    public void borrowBook() {
        this.componentBook.borrowBook();
    }

    @Override
    public void buyBook() {
        this.componentBook.buyBook();
    }
}

書的具體裝飾角色:

/**
 * 子類里寫了并且使用了無參的構(gòu)造方法但是它的父類(祖先)中卻至少有一個(gè)是沒有無參構(gòu)造方法的
 * @author tcy
 * @Date 10-08-2022
 */
public class ConcreteDecoratorBook1 extends DecoratorBook{

    ConcreteDecoratorBook1(ComponentBook componentBook) {
        super(componentBook);
    }

    public void cleanRoom(){
        System.out.println("打掃書房...");
    }

    public void shutRoom(){
        System.out.println("關(guān)閉書房...");
    }

    public void recordBook(){
        System.out.println("記錄借出記錄...");
    }

    public void returnBook(){
        System.out.println("收到借出去的書...");
    }

    @Override
    public void buyBook() {
        this.cleanRoom();
        super.buyBook();
        this.shutRoom();
        System.out.println("----------------------------");
    }

    @Override
    public void borrowBook() {
        this.recordBook();
        super.borrowBook();
        this.returnBook();
        System.out.println("----------------------------");
    }
}

如果讀者的Java基礎(chǔ)扎實(shí),理解裝飾器還是比較輕松的,裝飾器的實(shí)現(xiàn)方式很直觀,需要特別指出的是,在書的具體裝飾角色中,要顯示的定義一個(gè)構(gòu)造方法。

基礎(chǔ)不太扎實(shí)的讀者可能會(huì)有一個(gè)疑問,在Java的類中默認(rèn)不是會(huì)有一個(gè)無參的構(gòu)造方法嗎?為什么這里還需要定義呢?

在java中一個(gè)類只要有父類,那么在它實(shí)例化的時(shí)候,一定是從頂級(jí)的父類開始創(chuàng)建。

也就是說當(dāng)你用子類的無參構(gòu)造函數(shù)創(chuàng)建子類對(duì)象時(shí),會(huì)去先遞歸調(diào)用父類的無參構(gòu)造方法,這時(shí)候如果某個(gè)類的父類沒有無參構(gòu)造方法就會(huì)編譯出差。所以我們?cè)谧宇愔锌梢允謩?dòng)定義一個(gè)無參方法,或者在父類中顯示的定義一個(gè)構(gòu)造方法。

客戶端:

/**
 * @author tcy
 * @Date 09-08-2022
 */
public class  {
    public static void main(String[] args) {

        ComponentBook componentBook=new ConcreteComponentBook();
        componentBook=new ConcreteDecoratorBook1(componentBook);

        componentBook.borrowBook();

        componentBook.buyBook();
    }
}

方法調(diào)用后我們可以看到執(zhí)行結(jié)果:

記錄借出記錄...
老王的書借出去...
收到借出去的書...
----------------------------
打掃書房...
老王的書買回來...
關(guān)閉書房...
----------------------------

在老王借書和買書的這個(gè)事件中,成功的織入進(jìn)去小王的方法,這樣也就實(shí)現(xiàn)了裝飾器模式。

為了加強(qiáng)理解我們接著看裝飾器模式在我們經(jīng)常接觸的源碼中的運(yùn)用。

三、應(yīng)用

1、jdk中的應(yīng)用IO

裝飾器在java中最典型的應(yīng)用就是IO,我們知道在IO家族中有各種各樣的流,而流往往都是作用在子類之上,然后增加其附加功能,我們以InputStream 舉例。

InputStream 是字節(jié)輸入流,此抽象類是表示字節(jié)輸入流的所有類的超類。

FileInputStream是InputStream 的一個(gè)實(shí)現(xiàn)父類,BufferedInputStream是FileInputStream的實(shí)現(xiàn)父類。

實(shí)際BufferedInputStream就是裝飾者,InputStream 就是抽象構(gòu)件,F(xiàn)ileInputStream是具體構(gòu)件,BufferedInputStream就是對(duì)FileInputStream進(jìn)行了包裝。

我們看具體的應(yīng)用:

FileInputStream fileInputStream = new FileInputStream(filePath); 
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

直接將FileInputStream的實(shí)例傳給BufferedInputStream的構(gòu)造方法,就能調(diào)用BufferedInputStream增強(qiáng)的一些方法了。

我們具體看BufferedInputStream裝飾器類:

public class BufferedInputStream extends FilterInputStream {

    private static int DEFAULT_BUFFER_SIZE = 8192;

    protected int marklimit;

    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

  	...
    //增強(qiáng)的方法
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* grow buffer */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // Can't replace buf if there was an async close.
                    // Note: This would need to be changed if fill()
                    // is ever made accessible to multiple threads.
                    // But for now, the only way CAS can fail is via close.
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }

   ...

BufferedInputStream類中有許多其他的方法,就是對(duì)FileInputStream類的增強(qiáng)。

2、Spring中的運(yùn)用

Spring使用裝飾器模式有兩個(gè)典型的特征,一個(gè)是類名中含有Wrapper,另一類是含有Decorator,功能也即動(dòng)態(tài)的給某些類增加一些額外的功能。

TransactionAwareCacheDecorator是處理spring有事務(wù)的時(shí)候緩存的類,我們?cè)谑褂胹pring的cache注解實(shí)現(xiàn)緩存的時(shí)候,當(dāng)出現(xiàn)事務(wù)的時(shí)候,那么緩存的同步性就需要做相應(yīng)的處理了,于是就有了這個(gè)裝飾者。

public class TransactionAwareCacheDecorator implements Cache {

	//抽象構(gòu)件
   private final Cache targetCache;

   /**
    * Create a new TransactionAwareCache for the given target Cache.
    * @param targetCache the target Cache to decorate
    */
   public TransactionAwareCacheDecorator(Cache targetCache) {
      Assert.notNull(targetCache, "Target Cache must not be null");
      this.targetCache = targetCache;
   }

   /**直接調(diào)用未增強(qiáng)
    * Return the target Cache that this Cache should delegate to.
    */
   public Cache getTargetCache() {
      return this.targetCache;
   }

	//直接調(diào)用未增強(qiáng)
   @Override
   public String getName() {
      return this.targetCache.getName();
   }

//直接調(diào)用未增強(qiáng)
   @Override
   public Object getNativeCache() {
      return this.targetCache.getNativeCache();
   }

//直接調(diào)用未增強(qiáng)
   @Override
   @Nullable
   public ValueWrapper get(Object key) {
      return this.targetCache.get(key);
   }

//直接調(diào)用未增強(qiáng)
   @Override
   public <T> T get(Object key, @Nullable Class<T> type) {
      return this.targetCache.get(key, type);
   }

//直接調(diào)用未增強(qiáng)
   @Override
   @Nullable
   public <T> T get(Object key, Callable<T> valueLoader) {
      return this.targetCache.get(key, valueLoader);
   }

//先進(jìn)行判斷確定是否需要增強(qiáng)
   @Override
   public void put(final Object key, @Nullable final Object value) {
      if (TransactionSynchronizationManager.isSynchronizationActive()) {
         TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
               TransactionAwareCacheDecorator.this.targetCache.put(key, value);
            }
         });
      }
      else {
         this.targetCache.put(key, value);
      }
   }
}

Cache是抽象構(gòu)件,TransactionAwareCacheDecorator就是裝飾者,而Cache的實(shí)現(xiàn)類就是具體構(gòu)件。

因?yàn)椴⒎撬械姆椒ǘ紩?huì)使用事務(wù),有的普通方法就不需要裝飾,有的就需要,所以就使用了裝飾者模式來完成。

比如put()方法:

  @Override
   public void put(final Object key, @Nullable final Object value) {
      if (TransactionSynchronizationManager.isSynchronizationActive()) {
         TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
               TransactionAwareCacheDecorator.this.targetCache.put(key, value);
            }
         });
      }
      else {
         this.targetCache.put(key, value);
      }
   }

會(huì)在put前判斷是否開啟了事務(wù)TransactionSynchronizationManager.isSynchronizationActive(),如果開啟事務(wù)就調(diào)用一下額外的方法,如果沒有開始事務(wù)就調(diào)用默認(rèn)的方法。

我們舉的這個(gè)例子調(diào)用的就是 TransactionSynchronizationManager.registerSynchronization()方法,也即是為當(dāng)前線程注冊(cè)一個(gè)新的事務(wù)同步。

在Spring中將裝飾角色和具體裝飾角色合二為一,直接在裝飾者中實(shí)現(xiàn)要增加的方法。

3、MyBatista的運(yùn)用

了解過MyBatis的大致執(zhí)行流程的讀者應(yīng)該知道,Executor是MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語句的生成和查詢緩存的維護(hù);CachingExecutor是一個(gè)Executor的裝飾器,給一個(gè)Executor增加了緩存的功能。此時(shí)可以看做是對(duì)Executor類的一個(gè)增強(qiáng),故使用裝飾器模式是合適的。

我們首先看下Executor類的繼承結(jié)構(gòu)。

我們將關(guān)鍵的CachingExecutor代碼放上:

public class CachingExecutor implements Executor {
    //持有組件對(duì)象
  private Executor delegate;
  private TransactionalCacheManager tcm = new TransactionalCacheManager();
    //構(gòu)造方法,傳入組件對(duì)象
  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }
  @Override
  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
      //轉(zhuǎn)發(fā)請(qǐng)求給組件對(duì)象,可以在轉(zhuǎn)發(fā)前后執(zhí)行一些附加動(dòng)作
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
  }
  //...
 }

Executor就是抽象構(gòu)件,BaseExecutor是具體構(gòu)件的實(shí)現(xiàn),CachingExecutor就是裝飾角色,那具體裝飾角色在哪呢?

實(shí)際中具體裝飾角色直接在裝飾角色中集成了,并沒有將具體裝飾角色完全獨(dú)立出來。

另外,Mybatis的一級(jí)緩存和二級(jí)緩存也是使用的裝飾者模式,有興趣的讀者可以拉取Mybatis的源代碼本地進(jìn)行調(diào)試研究。

四、總結(jié)

到此為止,我們就將裝飾器模式的內(nèi)容講解清楚了,看到這讀者可能發(fā)現(xiàn),針對(duì)某一類需求可能會(huì)有很多設(shè)計(jì)模式都能完成需求,但一定是有最合適的那一個(gè),就像我們今天舉的例子無論是用裝飾器模式還是代理模式都可以實(shí)現(xiàn)這個(gè)需求。

但我們看代理模式中我們列舉的例子是以租房做例子,中介將房子的權(quán)利完全移交過去,中介完全控制房子做一些改造,今天書房的需求只是讓小王來幫忙的,還是以老王為主體,小王只是做一些附加。

裝飾器模式就是在瓶里面插了一朵花,而代理模式是把瓶子都給人家了,讓人家隨便折騰。

如果我們的需求是日志收集、攔截器,代理模式是最適合的。如果是動(dòng)態(tài)的增加對(duì)象的功能、限制對(duì)象的執(zhí)行條件、參數(shù)控制和檢查等使用適配器模式就更加合適了。

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請(qǐng)查看下面相關(guān)鏈接

相關(guān)文章

  • Spring Boot示例分析講解自動(dòng)化裝配機(jī)制核心注解

    Spring Boot示例分析講解自動(dòng)化裝配機(jī)制核心注解

    這篇文章主要分析了Spring Boot 自動(dòng)化裝配機(jī)制核心注解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-07-07
  • Java8 自定義CompletableFuture的原理解析

    Java8 自定義CompletableFuture的原理解析

    這篇文章主要介紹了Java8 自定義CompletableFuture的原理解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 自主配置數(shù)據(jù)源,mybatis/plus不打印sql日志問題

    自主配置數(shù)據(jù)源,mybatis/plus不打印sql日志問題

    這篇文章主要介紹了自主配置數(shù)據(jù)源,mybatis/plus不打印sql日志問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java存儲(chǔ)過程調(diào)用CallableStatement的方法

    Java存儲(chǔ)過程調(diào)用CallableStatement的方法

    這篇文章主要介紹了Java存儲(chǔ)過程調(diào)用CallableStatement的方法,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-11-11
  • Spring?Mvc中CommonsMultipartFile的特性實(shí)例詳解

    Spring?Mvc中CommonsMultipartFile的特性實(shí)例詳解

    這篇文章主要給大家介紹了關(guān)于Spring?Mvc中CommonsMultipartFile特性的相關(guān)資料,SpringMVC擁有強(qiáng)大的靈活性,非侵入性和可配置性,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • SpringBoot優(yōu)化接口響應(yīng)時(shí)間的九個(gè)技巧

    SpringBoot優(yōu)化接口響應(yīng)時(shí)間的九個(gè)技巧

    在實(shí)際開發(fā)中,提升接口響應(yīng)速度是一件挺重要的事,特別是在面臨大量用戶請(qǐng)求的時(shí)候,本文為大家整理了9個(gè)SpringBoot優(yōu)化接口響應(yīng)時(shí)間的技巧,希望對(duì)大家有所幫助
    2024-01-01
  • Java的動(dòng)態(tài)代理和靜態(tài)代理及反射常用API詳解

    Java的動(dòng)態(tài)代理和靜態(tài)代理及反射常用API詳解

    這篇文章主要介紹了Java的動(dòng)態(tài)代理和靜態(tài)代理及反射常用API詳解,動(dòng)態(tài)代理是一種在運(yùn)行時(shí)動(dòng)態(tài)生成代理對(duì)象的技術(shù),它是一種設(shè)計(jì)模式,用于在不修改原始對(duì)象的情況下,通過代理對(duì)象來間接訪問原始對(duì)象,并在訪問前后執(zhí)行額外的操作,需要的朋友可以參考下
    2024-01-01
  • 用java等語言仿360首頁拼音輸入全模糊搜索和自動(dòng)換膚

    用java等語言仿360首頁拼音輸入全模糊搜索和自動(dòng)換膚

    這篇文章主要為大家詳細(xì)介紹了仿360首頁支持拼音輸入全模糊搜索和自動(dòng)換膚的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Java API操作HDFS方法詳細(xì)講解

    Java API操作HDFS方法詳細(xì)講解

    這篇文章主要介紹了Java API操作Hdfs詳細(xì)示例,遍歷當(dāng)前目錄下所有文件與文件夾,可以使用listStatus方法實(shí)現(xiàn)上述需求,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • Android圖片轉(zhuǎn)換器代碼分享

    Android圖片轉(zhuǎn)換器代碼分享

    本文給大家總結(jié)了下在安卓程序中進(jìn)行圖片轉(zhuǎn)換的方法,非常的實(shí)用,小伙伴們可以參考下。
    2015-10-10

最新評(píng)論

临颍县| 太康县| 宝兴县| 阿尔山市| 深州市| 阳泉市| 手游| 黑山县| 屏东县| 尖扎县| 柞水县| 天水市| 湟源县| 柳江县| 乐平市| 志丹县| 凭祥市| 宁安市| 施甸县| 廉江市| 昌平区| 乡城县| 济宁市| 策勒县| 广东省| 大理市| 寿光市| 娄底市| 乐至县| 博野县| 博野县| 聊城市| 兴业县| 台山市| 阳东县| 开平市| 新化县| 临桂县| 兖州市| 乳山市| 崇明县|