关于设计模式:浅谈设计模式-模板方法十

44次阅读

共计 5500 个字符,预计需要花费 14 分钟才能阅读完成。

浅谈设计模式 – 模板办法(十)

前言:

​ 模板办法模式在 JAVA 当中最为熟知的就是 springtemplate对象,模板办法和策略这两个模式须要小心的辨别,对于模板办法模式只须要重点记忆一句话:模板办法的模式定义了算法的骨架 。同时针对模板办法的的一项设计准则 好莱坞准则 也是对 依赖倒转 准则一种很好的补充和扩大。

文章目标:

  1. 理解模板办法,同时理解模板办法是如何体现好莱坞准则的。
  2. 模板办法与策略模式的比照,以及模板办法灵活运用钩子函数。
  3. 模板办法的简略案例,以及在 spring 框架当中的具体体现。

什么是模板办法

根本定义

定义:在一个办法当中定义了算法的骨架,而将具体的实现提早到子类当中。模板办法在不扭转算法构造的状况下,重构算法的某些步骤。

从事实看模板办法

​ 咱们都晓得古代各式各样的蛋糕都是应用模具做成的,而同一个形态的蛋糕却能够应用不同的配料,此时模具便是模板办法的骨架,通过定义具体的配料细节对应了“算法”的细节。

钩子函数

​ 钩子函数是一种编程上比拟罕用的技巧,在框架设计当中非常常见,什么是钩子呢?从集体的了解来看,钩子像是能够提早定义的匿名函数,钩子能够“勾”住某个算法的两头过程,让外部环境能够干预外部算法实现的同时,又能让外部的函数进行自在管制钩子的应用。

​ 钩子函数个别实现形式为抽象类或者不做任何动作的函数。

​ 钩子函数在脚本语言外面常常被用作回调函数。包含 java 的许多框架也用钩子让用户能够干预一些算法的细节。然而须要留神的是,钩子这个货色很容易毁坏代码的可浏览性,所以不倡议常常应用这种函数,能够用组合以及其余的设计模式对于构造进行优化。

模板办法的结构图

​ 上面是模板办法的结构图,模板办法比照其余设计模式应该算是最简略的一个结构图了,比拟容易了解:

​ 模板办法的模式定义了算法的骨架,那么什么是定义算法的骨架,从上面的图表很好的看到,父类定义为抽象类,定义模板的算法办法和形象的算法细节。

这里要实现算法须要由子类实现具体的算法业务

模板办法的优缺点

长处:

  • 模板办法能够让算法的细节覆盖在子类,同时抽取公共的算法,进步代码复用水平
  • 模板办法能够让批改管制在子类,而父类办法不须要进行改变,合乎凋谢敞开准则。

毛病:

  • 模板办法类的改变对于所有的算法实现子类都会产生影响,同时模板父类改变违反“凋谢 - 敞开”准则
  • 模板办法因为利用钩子管制父类办法,会导致反向控制代码,对于代码的浏览不是非常敌对。

模板办法与好莱坞准则

什么是好莱坞准则?

 首先须要理解一下什么是好莱坞准则:** 让咱们调用你们,而不是让你们调用我 **。

和依赖倒转准则有什么关联?

​ 好莱坞准则更像是对于依赖倒转的一种扩大技巧。依赖倒转更加关注的是如何在设计中防止面向实现编程,而好莱坞则是将实现的调用在低层的构造进行暗藏。

为什么不倡议低层组件调用高层组件?

​ 为了避免环形依赖,在高层组件外面调用了形象办法,而形象办法又调用高层组件的办法。

策略模式和模板办法比照

策略模式和模板办法模式的比照

  1. 策略是定义一整个算法,应用组合的模式实现不同的对象切换
  2. 模板办法的是定义一个超类,在超类中通过高层调用底层实现的具体方法的实现,来实现办法的提早性能

案例

​ 这次的案例以集体小时候做过的一件事件举例,以前外婆兼职从厂里拿来一堆玩具整机的成品,而工作就是把成品进行“反转”(就是把做好的玩具翻面),还十分分明的记得大略是一分钱一个,靠着帮忙那时候还拿了一些零花钱,每天放学做完作业之后就是帮外婆做“兼职”。这种重复性劳动,在代码的构建很容易想到模板办法的模式,因为各种玩具的形态不同,所以翻面的形式以及效率和速度都不同,咱们将重复劳动的局部定义为顶层的模板,而具体的玩具构建细节,须要依据不同的玩具进行不同的操作,上面定义这个工作的大抵流程:

上面是依据结构图绘制一个根本的代码:

// 玩具制作模板类
public abstract class TemplateWorkFlow {public void productToy(){takeToy();
        reverseToy();
        putBasket();}

    public final void putBasket() {System.out.println("把玩具放到玩具篮");
    }

    public void takeToy(){System.out.println("拿起玩具");
    }

    public abstract void reverseToy();}

public class AntlersToyWorkFlow extends TemplateWorkFlow {
    @Override
    public void reverseToy() {System.out.println("把骨干翻面");
        System.out.println("把鹿角的分叉翻页");
    }
}

public class ChristmasHatWorkFlow extends TemplateWorkFlow{
    @Override
    public void reverseToy() {System.out.println("圣诞帽反转");
        System.out.println("圣诞帽帽子顶部的小秋顶进来");
    }
}

public class Main {public static void main(String[] args) {TemplateWorkFlow templateWorkFlow = new ChristmasHatWorkFlow();
        TemplateWorkFlow templateWorkFlow1 = new AntlersToyWorkFlow();
        templateWorkFlow.productToy();
        templateWorkFlow1.productToy();}/*
    拿起玩具
    圣诞帽反转
    圣诞帽帽子顶部的小秋顶进来
    把玩具放到玩具篮
    拿起玩具
    把骨干翻面
    把鹿角的分叉翻页
    把玩具放到玩具篮

    */
}

如果不应用设计模式,他大抵的设计代码如下,能够看到很多办法都干了类似的事件,这些办法可能实质上只是一两行代码甚至只是取名不一样,当然古代的编译器都很“聪慧”,会发现反复的点,所以最最根本的要求,是编写出编译器都无奈发现的反复代码,当然仅仅凭借这一点显然要求有点低

​ 上面看下不应用模板办法的代码:

public class ChristmasHatWorkFlow{public void productToy(){takeToy();
        reverseToy();
        putBasket();}

    public final void putBasket() {System.out.println("把玩具放到玩具篮");
    }

    public void takeToy(){System.out.println("拿起玩具");
    }

    public void reverseToy() {System.out.println("圣诞帽反转");
        System.out.println("圣诞帽帽子顶部的小秋顶进来");
    }
}

public class AntlersToyWorkFlow {public void productToy(){takeToy();
        reverseToy();
        putBasket();}

    public final void putBasket() {System.out.println("把玩具放到玩具篮");
    }

    public void takeToy(){System.out.println("拿起玩具");
    }

    public void reverseToy() {System.out.println("把骨干翻面");
        System.out.println("把鹿角的分叉翻页");
    }
}

public class Main {public static void main(String[] args) {AntlersToyWorkFlow antlersToyWorkFlow = new AntlersToyWorkFlow();
        antlersToyWorkFlow.productToy();
        ChristmasHatWorkFlow christmasHatWorkFlow = new ChristmasHatWorkFlow();
        christmasHatWorkFlow.productToy();}/*
    拿起玩具
    把骨干翻面
    把鹿角的分叉翻页
    把玩具放到玩具篮
    拿起玩具
    圣诞帽反转
    圣诞帽帽子顶部的小秋顶进来
    把玩具放到玩具篮

    */
}

spring 当中的模板办法

spring 最为典型的案例便是 Tempalte 框架,然而须要留神 spring 少数状况下并没有应用经典的模板办法构造,而是应用了 CallBack 函数 的模式,避开了继承构造的同时,每个类能够独自实现本人的具体性能:

咱们看一下 RedisTempalte 当中的StringRedisTemplate,这里调用父类的afterPropertiesSet()

public class StringRedisTemplate extends RedisTemplate<String, String> {

   /**
    * Constructs a new <code>StringRedisTemplate</code> instance ready to be used.
    *
    * @param connectionFactory connection factory for creating new connections
    */
   public StringRedisTemplate(RedisConnectionFactory connectionFactory) {this();
      setConnectionFactory(connectionFactory);
      afterPropertiesSet();}

   protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {return new DefaultStringRedisConnection(connection);
   }
}

父类同样继承的伎俩,在如下办法当中调用了afterProperteisSet(),通过 super 援用父类的办法:

@Override
public void afterPropertiesSet() {
    // 留神
   super.afterPropertiesSet();

   boolean defaultUsed = false;

   if (defaultSerializer == null) {

      defaultSerializer = new JdkSerializationRedisSerializer(classLoader != null ? classLoader : this.getClass().getClassLoader());
   }

   if (enableDefaultSerializer) {if (keySerializer == null) {
         keySerializer = defaultSerializer;
         defaultUsed = true;
      }
      if (valueSerializer == null) {
         valueSerializer = defaultSerializer;
         defaultUsed = true;
      }
      if (hashKeySerializer == null) {
         hashKeySerializer = defaultSerializer;
         defaultUsed = true;
      }
      if (hashValueSerializer == null) {
         hashValueSerializer = defaultSerializer;
         defaultUsed = true;
      }
   }

   if (enableDefaultSerializer && defaultUsed) {Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
   }

   if (scriptExecutor == null) {this.scriptExecutor = new DefaultScriptExecutor<>(this);
   }

   initialized = true;
}

上面援用父类 RedisAccesor 类当中的afterPropertiesSet()

public void afterPropertiesSet() {Assert.state(getConnectionFactory() != null, "RedisConnectionFactory is required");
}

通过定义 getConnectionFactory() 办法,子类能够自在的配置连贯工厂,也能够间接沿用父类的默认实现。

总结

​ 模板办法是一个比拟重要的设计模式,他能够从构造上帮忙程序员构建一个良好的抽象概念,同时模板办法提供的钩子函数,通过定义形象办法提早到子类实现这一技巧十分合乎“凋谢 - 敞开”准则,灵活运用模板办法模式有利于构建更加灵便的软件骨架,同时能够定义各种多变的算法体系。然而须要留神的是传统的模板办法这种继承的构造 并不推崇,因为咱们都晓得继承对于所有子类都会产生影响。

​ 另外模板办法这个模式对于浏览代码的体验不是很好,常常须要各个类之间一直切换,有时候甚至会莫名其妙为什么忽然跑到另一个办法外面,模板办法有时候比拟影响浏览体验。

​ 另外设计模式最大的目标就是 缩小反复代码 以及 用最小的代价进行扩大,集体认为如果代码合乎这两个点根本就是一个好代码,然而这个点的确日常工作最难实现的。

正文完
 0