天天看点

设计模式速记-模板方法模式

设计模式速记-模板方法模式

模板方法模式

模板方法模式的定义:

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

简而言之,模板方法模式就是已知并且确定整个算法的流程,但是算法流程中某些过程需要特定情况特定处理。

例如要获取音乐最受欢迎排行榜前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  
               

参考资料

  • 模板方法模式
  • 模板方法模式