天天看點

設計模式速記-模闆方法模式

設計模式速記-模闆方法模式

模闆方法模式

模闆方法模式的定義:

定義一個操作中的算法骨架,而将算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。它是一種類行為型模式。

簡而言之,模闆方法模式就是已知并且确定整個算法的流程,但是算法流程中某些過程需要特定情況特定處理。

例如要擷取音樂最受歡迎排行榜前100名,那麼算法的流程分成三步(使用排序方法):

  1. 将入選音樂彙總
  2. 将入選音樂排序
  3. 選擇排序榜前100名

确定了骨架之後,三個步驟中的任何一部分都可以随着需求更換,比如

  • 入選音樂可以選擇流行音樂或者古典音樂。
  • 排序通過順序和倒序生成的榜單分别是最受歡迎和最不受歡迎
  • 通過改變榜單音樂數來更改榜單大小

這時候隻需要在子類中實作每個需求獨立的算法,就需要用到模闆方法模式。

模式的優缺點

優點

  • 它封裝了不變部分,擴充可變部分。它把認為是不變部分的算法封裝到父類中實作,而把可變部分算法由子類繼承實作,便于子類繼續擴充。
  • 它在父類中提取了公共的部分代碼,便于代碼複用。
  • 部分方法是由子類實作的,是以子類可以通過擴充方式增加相應的功能,符合開閉原則。

缺點

  • 對每個不同的實作都需要定義一個子類,這會導緻類的個數增加,系統更加龐大,設計也更加抽象。
  • 父類中的抽象方法由子類實作,子類執行的結果會影響父類的結果,這導緻一種反向的控制結構,它提高了代碼閱讀的難度。

模式角色

  1. 抽象類

    抽象類中定義算法骨架,并且定義抽象的子過程方法

    • 模闆方法:算法骨架
    • 子過程方法
      • 抽象方法:在抽象類中申明,由具體子類實作。
      • 具體方法:在抽象類中已經實作,在具體子類中可以繼承或重寫它。
      • 鈎子方法:在抽象類中已經實作,包括用于判斷的邏輯方法和需要子類重寫的空方法兩種。
  2. 具體子類

    實作抽象類中的抽象方法和鈎子方法,目的在于完善需求的子過程。

示例

場景描述

以上述音樂排行榜需求,擷取最受歡迎排行榜前十名,步驟如下:
  1. 擷取音樂源資料
  2. 根據音樂播放數目排序
  3. 擷取最受歡迎排行榜和最不受歡迎排行榜前10名

示例代碼

  • 抽象類
    /**
     * @Description 音樂資料管理,抽象類角色
     */
    public abstract class MusicDataManager {
    
        /**
         * 擷取最佳音樂排行榜單
         * @param musicDataList 入選音樂資料
         * @param count 最佳榜單音樂數目
         * @return 榜單
         */
        public List<MusicData> musicList(List<MusicData> musicDataList,int count){
            receiveMusicList(musicDataList);
            sortMusicList();
            return rangeMusicList(count);
        }
    
        /**
         * 擷取音樂資料
         * @param musicDataList 音樂資料表
         */
        protected abstract void receiveMusicList(List<MusicData> musicDataList);
    
        /**
         * 對音樂資料進行排序
         */
        protected abstract void sortMusicList();
    
        /**
         * 擷取音樂榜單
         * @param count 榜單音樂數量
         * @return 音樂榜單
         */
        protected abstract List<MusicData> rangeMusicList(int count);
    }
               
    /**
     * @Description 音樂資料Bean
     */
    @Data
    @AllArgsConstructor
    public class MusicData {
        private String name;
        private int visitCount;
    }
               
  • 具體子類
    /**
     * @Description 最受歡迎排行榜,具體子類
     */
    public class TopPopularMusic extends MusicDataManager{
    
        private List<MusicData> musicDataList;
    
        @Override
        protected void receiveMusicList(List<MusicData> musicDataList) {
            this.musicDataList = musicDataList;
        }
    
        @Override
        protected void sortMusicList() {
            /**
             * 最高音樂榜單根據通路數量逆序排序
             */
            this.musicDataList.sort((d1 , d2) -> d2.getVisitCount() - d1.getVisitCount());
        }
    
        @Override
        protected List<MusicData> rangeMusicList(int count) {
            return this.musicDataList.subList(0 , count);
        }
    }
               
    /**
     * @Description 最不受歡迎音樂排行榜
     */
    public class UnpopularityMusic extends MusicDataManager {
    
        private List<MusicData> musicDataList ;
    
        @Override
        protected void receiveMusicList(List<MusicData> musicDataList) {
            this.musicDataList = musicDataList;
        }
    
        @Override
        protected void sortMusicList() {
            this.musicDataList.sort(Comparator.comparingInt(MusicData::getVisitCount));
        }
    
        @Override
        protected List<MusicData> rangeMusicList(int count) {
            return this.musicDataList.subList(0 , count);
        }
    }
               
  • 測試代碼
    /**
     * @Description 模闆方法模式測試
     */
    public class TemplateTest {
    
        @Test
        public void test() {
            List<MusicData> musicData = initMusicData();
    
            MusicDataManager topMusicDataManager = new TopPopularMusic();
            List<MusicData> musicData1 = topMusicDataManager.musicList(musicData, 10);
            prtData("最受歡迎音樂清單", musicData1);
    
            MusicDataManager unpopularityManager = new UnpopularityMusic();
            List<MusicData> musicData2 = unpopularityManager.musicList(musicData, 10);
            prtData("最不受歡迎音樂清單", musicData2);
        }
    
        void prtData(String message, List<MusicData> dataList) {
            System.out.println(message);
            dataList.forEach((data) -> System.out.println(String.format("%-20s%-20s", data.getName(), data.getVisitCount())));
        }
    
        /**
         * 初始化音樂資料清單
         *
         * @return 音樂資料
         */
        public List<MusicData> initMusicData() {
            return Lists.newArrayList(
                    new MusicData("Dogs", 20),
                    new MusicData("Misunderstood", 39),
                    new MusicData("Give Me Love", 329),
                    new MusicData("Dream", 202),
                    new MusicData("Shape Of You", 88),
                    new MusicData("Good Vibes", 101),
                    new MusicData("Yesterday", 339),
                    new MusicData("Sold out", 195),
                    new MusicData("Victory", 561),
                    new MusicData("Longing", 213),
                    new MusicData("Lily", 195),
                    new MusicData("This Empty Space", 564),
                    new MusicData("17", 1231),
                    new MusicData("Unspoken", 156),
                    new MusicData("Evergreen", 546)
            );
        }
    }
               
  • 測試結果
    最受歡迎音樂清單
    17                  1231                
    This Empty Space    564                 
    Victory             561                 
    Evergreen           546                 
    Yesterday           339                 
    Give Me Love        329                 
    Longing             213                 
    Dream               202                 
    Sold out            195                 
    Lily                195                 
    最不受歡迎音樂清單
    Dogs                20                  
    Misunderstood       39                  
    Shape Of You        88                  
    Good Vibes          101                 
    Unspoken            156                 
    Sold out            195                 
    Lily                195                 
    Dream               202                 
    Longing             213                 
    Give Me Love        329  
               

參考資料

  • 模闆方法模式
  • 模闆方法模式