抽象是面向对象编程的核心思想,从某种角度来看,抽象,就是把可变的部分和不可变部分分离开来,今天介绍的模板方法模式,体现的就是这样一种思想。总结起来,模板方法模式可以用一句话来概括:以不变应万变。
1.模板方法模式
模板方法模式(Template Method Pattern),定义了一个算法的骨架,将一些具体步骤的实现推迟到子类中。
模板方法设计模式的核心思想就是将“可变部分”与“不可变部分”分离,通常,在一个抽象基类中定义模板方法,该方法即算法不可变部分,其中调用了一系列抽象方法,这些可重写的抽象方法即可变部分,由其子类重写。因此,模板方法模式可以在不改变算法结构的情况下,重新调整算法中的某些步骤。
模版方法模式的UML类图:
图中,
templateMethod() 模板方法
action1,action2 是需要子类重写的抽象方法
2.代码实现
假设汽车由一条生产线装备(现实中是多条生产线),这条生产线上有各个机器人,入负责底盘的机器人,负责装配发动机的机器人,负责装配轮胎的机器人,负责装配车身的机器人。很显然,机器人可以抽象出一个类代表机器人这个抽象体,每个具体机器人都负责装配某部件。那么,可以在抽象机器人类中,定义一个模版方法,该方法定义了所有机器人装配零件时的通用步骤(算法),而具体细节由各个机器人实现。
首先,定义Car类,即汽车,我们的装配对象。 用布尔类型的属性记录是否已装配了相应零件的状态。
/** * 汽车类,装配对象 */ class Car { private static int count = 0; private final int id; public Car() { id = count++; } /** 汽车底盘 */ private boolean chassis = false; /** 发动机*/ private boolean engine = false; /** 轮胎 */ private boolean wheels = false; /** 车身 */ private boolean body = false; /** 安装底盘*/ public void setChassis(){ chassis = true; } /** 安装发动机*/ public void setEngine(){ engine = true; } /** 安装轮胎*/ public void setWheels() { wheels = true; } /** 安装车身 */ public void setBody() { body = true; } public String toString() { return "car" + id + "( chassis:" + chassis + ", engine:" + engine + ", wheels:" + wheels + ", body:" + body + ")" ; } }
定义机器人抽象类,有成员属性Assembler,即装配线的引用,该装配线与本例中的模版方法模式无关。定义了模版方法:assemble(),即装配,该方法中设计了一些具体机器人的共通逻辑,而调用的performance(0,则将具体装配推迟到了子类中。
/** * 机器人抽象类,提供模板方法assemble,以及一系列抽象方法 */ abstract class Robot { protected Assembler assembler; public void setAssembler(Assembler assembler) { this.assembler = assembler; } /** 模板方法 */ protected void assemble() { System.out.println("开始装配:" + assembler.car); performance(); System.out.println("装配完成:" + assembler.car); } protected abstract void performance(); }
各个具体机器人,实现了“可变”的方法performance()
/** * 底盘装配机器人 */ class ChassisRobot extends Robot{ @Override protected void performance() { assembler.car.setChassis(); } } /** * 发动机装配机器人 */ class EngineRobot extends Robot { @Override protected void performance() { assembler.car.setEngine(); } } /** * 轮胎装配机器人 */ class WheelsRobot extends Robot { @Override protected void performance() { assembler.car.setWheels(); } } /** * 车身装配机器人 */ class BodyRobot extends Robot { @Override protected void performance() { assembler.car.setBody(); } }
Assembler,为了更好地完成调度和管理
/** * 生产线,调度机器人装配 */ class Assembler { List<Robot> robots; Car car; public Assembler(Car car) { this.car= car; //注册机器人 robots = new ArrayList<>(); robots.add(new ChassisRobot()); robots.add(new EngineRobot()); robots.add(new WheelsRobot()); robots.add(new BodyRobot()); } public void run() { for (Robot robot : robots) { robot.setAssembler(this); robot.assemble(); } } }
客户端调用,首先创建Car实体,将其传入生产线Assembler,生产线run起来,即开始装配汽车零件。
public class TemplateDemo { public static void main(String[] args) { Car car = new Car(); Assembler assembler = new Assembler(car); assembler.run(); } }
输出结果:
开始装配:car0( chassis:false, engine:false, wheels:false, body:false) 装配完成:car0( chassis:true, engine:false, wheels:false, body:false) 开始装配:car0( chassis:true, engine:false, wheels:false, body:false) 装配完成:car0( chassis:true, engine:true, wheels:false, body:false) 开始装配:car0( chassis:true, engine:true, wheels:false, body:false) 装配完成:car0( chassis:true, engine:true, wheels:true, body:false) 开始装配:car0( chassis:true, engine:true, wheels:true, body:false) 装配完成:car0( chassis:true, engine:true, wheels:true, body:true)
3.总结
这里再强调一遍,用一句话概括模版方法模式就是:以不变应万变。这其中体现出的是面向对象编程当中最基本和最重要的思想:抽象。模版方法模式通过分离可变部分和不可变部分,实现了代码的解耦,使扩展和维护更加方便,模版方法模式在实际开发中大量应用,尽管很多时候是无意的。我想之所以要学习程序设计模式,并不是为了死记硬背其流程和步骤,应该体会一下各个模式背后的思想精髓,才能对编程这件事情有更加深刻的理解。