设计模式——模板方法模式
小时候每次考试的时候,都希望考卷都是写好的,每个人发一张,然后大家写上自己名字交卷子就可以了23333。
1.情景举例
公司又要制造机器了,我们的麻烦也就又来了。
这两天,来了个新活儿,有个餐厅的一角放几个自助饮料机。经过调查之后,发现员工们普遍喜欢咖啡,橙汁,茶水。于是公司接了这个活。设计团队首先定义了一个所有饮料机的父类,以便统一管理他们。
public abstract class DrinkMachine {
private String drinkName;
public DrinkMachine(String drinkName) {
this.drinkName = drinkName;
}
public abstract void getDrink();
}
每个具体的饮料需要实现getDrink()方法即可。然后小组长让每个组员实现一个具体的硬料机。
public class CoffeeMachine extends DrinkMachine {
public CoffeeMachine() {
super("Coffee");
}
@Override
public void getDrink() {
System.out.println("加入水");
System.out.println("加入咖啡豆");
System.out.println("研磨");
System.out.println("开启水阀");
System.out.println(drinkName +"流出");
System.out.println("关闭水阀");
}
}
public class JuiceMachine extends DrinkMachine {
public JuiceMachine(String drinkName) {
super("OrangeJuice");
}
@Override
public void getDrink() {
System.out.println("加入水");
System.out.println("加入橘子果肉");
System.out.println("榨汁混合");
System.out.println("开启水阀");
System.out.println(drinkName +"流出");
System.out.println("关闭水阀");
}
}
public class TeaMachine extends DrinkMachine {
public TeaMachine(String drinkName) {
super("Tea");
}
@Override
public void getDrink() {
System.out.println("加入水");
System.out.println("加入茶叶");
System.out.println("冲泡");
System.out.println("开启水阀");
System.out.println(drinkName +"流出");
System.out.println("关闭水阀");
}
}
三个组员忙了一晚上,终于完成了各自的任务(其实实际任务并不是打印流程这么简单的)。第二天他们在一起讨论的时候的发现三个人晚上干的活有50%是重复的,于是他们对组长安排的任务产生了怀疑。在经过学习之后,他们准备通过模板方法模式重构代码,然后给组长一个惊喜。
2.使用模板方法模式
首先组员A偷偷把组长写好的父类进行了修改。
public abstract class DrinkMachine {
private String drinkName;
public DrinkMachine(String drinkName) {
this.drinkName = drinkName;
}
public void getDrink() {
enterWater();
makeDrink();
setWaterValue();
};
public void enterWater() {
System.out.println("加入水");
};
public abstract void makeDrink();
public void setWaterValue() {
System.out.println("开启水阀");
System.out.println(drinkName +"流出");
System.out.println("关闭水阀");
}
}
整个制作饮料的过程被分成了三步,其中的两步都是固定的,因此在父类就已经实现了。剩下的一步是和饮料的类型有关的,因此要根据子类的不同有不同的实现,所以把makeDrink()方法设置为抽象方法,等待子类实现。父类还更改了getDrink()方法,在该方法中调用其他三步方法获得饮料,把该方法作为用户使用的接口。
其他两个组员则是对3个子类进行了改造,因为公共的部分已经由父类实现了,现在他们只需要实现每个子类中特有的部分就可以了。
public class CoffeeMachine extends DrinkMachine {
public CoffeeMachine() {
super("Coffee");
}
@Override
public void makeDrink() {
System.out.println("加入咖啡豆");
System.out.println("研磨");
}
}
public class JuiceMachine extends DrinkMachine {
public JuiceMachine() {
super("OrangeJuice");
}
@Override
public void makeDrink() {
System.out.println("加入橘子果肉");
System.out.println("榨汁混合");
}
}
public class TeaMachine extends DrinkMachine {
public TeaMachine() {
super("Tea");
}
@Override
public void makeDrink() {
System.out.println("加入茶叶");
System.out.println("冲泡");
}
}
大功告成之后他们发现每个子类中的重复代码大大的减少了,而且就算以后要制作新的饮料机,继承饮料机父类就能得到公共的功能(加入水的操作和开启、关闭水阀门的操作),然后只需要编写这个饮料机特定的操作代码即可。
他们高兴坏了,准备立刻测试一下。
public class MainTest {
public static void main(String[] args) {
ArrayList<DrinkMachine> list = new ArrayList<>(3);
list.add(new CoffeeMachine());
list.add(new JuiceMachine());
list.add(new TeaMachine());
for (DrinkMachine drinkMachine : list) {
drinkMachine.getDrink();
System.out.println();
}
}
}
和预期的一样完美,他们已经迫不及待的给组长进行一份炫耀了。
3.模板方法模式小结
模板方法定义了一个模板。在这个模板中完成了公共代码的实现,留出等待子类生成的空缺的部分,子类只需要根据自己的特性补全空缺的部分即可。
模板方法模式大大减少了继承体系中的重复代码,而且减小了整体出错率(没办法,有些人就是抄也能抄错)。
在实际开发中,如果发现一个继承体系的每个子类的整套功能在宏观上具有一致性,并且有着部分相同的动作,那么就可以尝试使用模板方法模式去重新构建这个继承结构。可以提高开发的效率,还能增加系统扩展性和可维护性。