豆浆制作问题
1.流程:选材 添加配料 浸泡 放入豆浆机搅碎
2.通过添加不同的配料,可以制作不同口味的豆浆
3.其中选材、浸泡、放入豆浆机搅碎对于制作每一种口味的豆浆机都是一致的。
模板方法模式
公开它方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类定义的方式进行。
即定义了一个操作中的算法骨架,将一些步骤的实现延迟给子类,使得子类可以在不改变一个算法的结构,便可定义某些特定步骤。
模板方法模式结构
1.抽象类 (AbstractClass)
会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为 抽象类型, 也可以提供一些默认实现。
2.具体类(ConcreteClass)
可以重写所有步骤,但不能重写模板方法本身。
模板方法模式适合应用场景
1.当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。
2.当多个类的算法除一些细微不同之外几乎完全一样时, 你可使用该模式。 但其后果就是, 只要算法发生变化, 你就可能需要修改所有的类
实现方式
- 分析目标算法, 确定能否将其分解为多个步骤。 从所有子类的角度出发, 考虑哪些步骤能够通用, 哪些步骤各不相同。
- 创建抽象基类并声明一个模板方法和代表算法步骤的一系列抽象方法。 在模板方法中根据算法结构依次调用相应步骤。 可用
final
最终修饰模板方法以防止子类对其进行重写。 - 虽然可将所有步骤全都设为抽象类型, 但默认实现可能会给部分步骤带来好处, 因为子类无需实现那些方法。
- 可考虑在算法的关键步骤之间添加钩子。
- 为每个算法变体新建一个具体子类, 它必须实现所有的抽象步骤, 也可以重写部分可选步骤。
模板方法模式优缺点
优点:
✔️ 重写一个大型算法中的特定部分, 使得算法其他部分修改对其所造成的影响减小。
✔️ 你可将重复代码提取到一个超类中。
缺点:
❌端可能会受到算法框架的限制。
❌子类抑制默认步骤实现可能会导致违反_里氏替换原则_。
❌方法中的步骤越多, 其维护工作就可能会越困难。
解决豆浆制作问题
类图
代码
public abstract class Soya {
//模板方法,若不想被子类重写,可添加final
final void make(){
select();
addCondiments();
soak();
beat();
}
//选材料
void select(){
System.out.println("选择了好材料");
}
//添加不同的配料,由于不同的豆浆,有不同的配料,于是由子类实重写
abstract void addCondiments();
//浸泡
void soak(){
System.out.println("将所需材料都浸泡");
}
//打碎
void beat(){
System.out.println("将所有材料搅碎");
}
}
//红豆豆浆
public class RedBeanSoya extends Soya {
@Override
void addCondiments() {
System.out.println("添加 红豆");
}
}
//花生豆浆
public class PeanutSoya extends Soya {
@Override
void addCondiments() {
System.out.println("添加 花生");
}
}
public class Client {
public static void main(String[] args) {
//红豆豆浆
Soya redBean = new RedBeanSoya();
redBean.make();
//花生豆浆
Soya peanut = new PeanutSoya();
peanut.make();
}
}
注:
钩子方法
在模板方法模式中,我们可以定义一个方法。它默认不做任何事情,子类则可以依据情况重写,这类方法称为“钩子”
eg:
豆浆项目中的纯豆浆
public abstract class Soya {
//模板方法,若不想被子类重写,可添加final
final void make(){
select();
//若添加材料函数为true,则允许添加材料方法
if (condimentsIf()){
addCondiments();
}
soak();
beat();
}
//选材料
void select(){
System.out.println("选择了好材料");
}
//添加不同的配料,由于不同的豆浆,有不同的配料,于是由子类实重写
abstract void addCondiments();
//浸泡
void soak(){
System.out.println("将所需材料都浸泡");
}
//打碎
void beat(){
System.out.println("将所有材料搅碎");
}
//钩子方法,决定是否添加配料
boolean condimentsIf(){
return true;
}
}
public class PureSoya extends Soya{
@Override
void addCondiments() {
//空实现、即不加材料
}
//重写钩子方法,声明不加材料
@Override
boolean condimentsIf() {
return false;
}
}
public class Client {
public static void main(String[] args) {
Soya pure = new PureSoya();
pure.make();
}
}
模板方法模式的注意事项和细节
1.基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修 改
2.实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
3.既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
4.该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
5.一 般模板方法都加上 final 关键字, 防止子类重写模板方法
6.模板方法模式使用场景:当要完成在某个过程 该过程要执行一系列步骤这一
系列的步骤基本相同 ,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理