前篇——软件设计模式-基础
前篇——软件设计模式-三种工厂模式
前篇——软件设计模式-装饰者模式
前篇——软件设计模式-单例模式
前篇——软件设计模式-原型模式
前篇——软件设计模式-命令模式
前篇——软件设计模式-策略模式
它是一种类行为型模式
目录
1.问题引入
泡茶与泡咖啡的流程
泡茶:烧水、加茶叶、倒水、加柠檬。
泡咖啡:烧水、加咖啡粉、倒水、加糖或者牛奶。
冲泡方法大致相似,我们将共同的部分抽取出来,放入一个基类中。
2.模板方法模式
定义算法架构的模式,步骤延迟到子类
2.1 定义
定义一个操作中的算法骨架,而将一些步骤延迟到子类之中,模板方法子类可以不改变一个算法的结构,即可重新定义该算法的某些特定步骤。
2.2 模板方法模式实现
2.2.1 e.g.1(实现问题)
模板方法实现引入的问题
//抽象模板(AbstractDrink)
public abstract class AbstractDrink{
//抽象方法(增加原料)
public abstract void add_X();
//抽象方法(增加其他的东西)
public abstract void add_Other();
//给钩子方法判断的变量
public String result;
//钩子方法(还需要其他东西吗)
public boolean needOtherThing(){
if(result == "yes"){
System.out.println(result);
return true;
}
else if(result == "no"){
System.out.println(result);
return false;
}else
System.out.println(result);
return false;
}
//模板方法(使用直接调用就完事了)
public void templeMethod(){
boilWater();
add_X();
pourInCup();
if(needOtherThing()) {
add_Other();
}
else{
System.out.println("原味即可,啥也不用加");
}
}
//基本方法(功能共通部分)(烧水)
public final void boilWater(){
System.out.println("把水烧开");
}
//基本方法(功能共通部分)(倒在杯子里)
public final void pourInCup(){
System.out.println("倒在杯子里");
}
}
//具体模板(咖啡)
public class Coffee extends AbstractDrink{
//在构造方法中给父类的result赋值,判断是否要加点糖,相当于顾客说的话
public Coffee(String result) {
super.result = result;
}
public static String coffee = "咖啡";
public static String suger = "糖";
//实现的抽象方法add_X,根据这个方法决定到底做什么
public void add_X(){
System.out.println("加入原材料:"+ coffee);
}
//实现辅料的抽象方法
public void add_Other(){
System.out.println("辅助添加:"+ suger );
}
}
//具体模板(茶)
//解释和上面咖啡类似
public class Tea extends AbstractDrink{
public Tea(String result) {
super.result = result;
}
public static String tea = "茶";
public static String lemon = "柠檬";
public void add_X(){
System.out.println("加入原材料:" + tea);
}
public void add_Other(){
System.out.println("辅助添加:" + lemon);
}
}
//测试类
public class test{
public static void main(String[] args) {
Coffee coffee1 = new Coffee("yes");
coffee1.templeMethod();
System.out.println("-------------------");
AbstractDrink tea1 = new Tea("no");
tea1.templeMethod();
}
}
2.3 结构
2.3.1 角色
- 抽象模板(AbstractDrink):抽象模板是一个抽象类。抽象模板定义了若干个方法表示一个算法的各个步骤。这些方法既有抽象方法,也有非抽象方法。在抽象模板中还定义了一个称作模板方法的方法。模板方法就是可以包含有具体抽象方法的调用,也可以包含有抽象方法的调用。
- 具体模板(Tea、Coffee):是抽象模板的子类,实现抽象模板中的抽象方法。
2.3.2 抽象模板中的方法
a. 模板方法(templeMethod()):是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法,并按照某种顺序调用包含的基本方法
b. 基本方法:基本方法是实现算法各个步骤的方法,是模板方法的组成部分
- 抽象方法(add_X()、add_Other()):在抽象类中声明,由具体子类实现
- 具体方法(boilWater(),pourInCup()):在抽象类中已经实现,在具体子类中可以继承或重写它。
- 钩子方法(needOtherThing()):在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
用final来修饰父类中不变(不能被重写)的方法
模板方法模式封装了一个算法步骤,并允许子类为一个或多个步骤方法提供实现,模板模式可以在使子类在不改变算法结构的情况下,重新定义算法中的某些步骤。
2.4 优缺点
2.4.1 优点
- 易扩展:一般抽象类中模板方法不易改变,抽象方法易改变,通过增加实现类,一般很容易扩展,符合开闭原则。
- 便于维护
- 灵活:因为钩子方法,子类的实现可以影响父类中逻辑的运行
2.4.2 缺点
- 由灵活引发的缺点:子类影响父类,给程序带来了风险
- 使系统过于庞大:每个不同的实现都要定义一个子类。
- 代码难读(子类父类相互影响)
2.5 应用场景
- 当子类有统一的操作步骤或操作过程
- 子类具有不同的操作细节
- 存在多个具有同样操作步骤的应用场景,但某些具体的操作细节不同
3. tip
3.1 策略模式 和 模板方法模式
细心的童鞋发现策略模式和模板方法模式都是对算法进行处理。
3.1.1 两者的区别
策略模式着重点在:使用委派的方法提供不同的算法行为。核心是:方法的封装
模板方法模式着重点在:使用继承的方法提供不同的算法行为。核心是:方法调用的顺序
3.1.2 两者的联系
可以组合使用:模板方法重在封装算法骨架。策略模式重在封装算法的实现。
3.2 final关键字
final知识点来自——“努力的小海龟”的博客
final关键字是我们经常使用的关键字之一,它的用法有很多,但是并不是每一种用法都值得我们去广泛使用。它的主要用法有以下四种:
- 用来修饰数据,包括成员变量和局部变量,该变量只能被赋值一次且它的值无法被改变。对于成员变量来讲,我们必须在声明时或者构造方法中对它赋值;
- 用来修饰方法参数,表示在变量的生存期中它的值不能被改变;
- 修饰方法,表示该方法无法被重写;
- 修饰类,表示该类无法被继承。
上面的四种方法中,第三种和第四种方法需要谨慎使用,因为在大多数情况下,如果是仅仅为了一点设计上的考虑,我们并不需要使用final来修饰方法和类。
3.3 模板方法模式迪米特法则
在模板方法模式中,子类不显示调用父类的方法,而是通过覆盖父类的方法来实现某些具体的业务逻辑。
3.3.1 好莱坞原则
父类控制对子类的调用。
3.3.2 遗留问题
在e.g.1中我们通过子类的构造方法来影响父类的行为,在一定程度违反了好莱坞原则。