一、模版方法模式定义和特点
在Spring源码中该设计模式被频繁引用,所以在看Spring源码时通常是绕来绕去,想找到业务实际的执行代码需要熟悉整体的继承结构。有些通用的内容父类进行封装,多变的内容定义成钩子方法,由继承的子类来实现。
模板方法(Template Method)模式的定义:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤,它是一种类行为型模式。
该模式的主要优点
1.对不变部分内容进行封装,提供钩子方法方便于子类扩展可变部分。
2.由于父类定义了公共的代码部分,有利于代码复用。
3.父类整体流程中对某一部分是由子类实现的,故子类可以通过扩展方式增加相应的功能,符合开闭原则。
该模式的主要缺点
1.对每个不同的业务实现都需要定义一个子类,这会导致项目中Java类的个数增加,系统显得更厚重,设计看起来很抽象。
2.父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,属于类的反向的控制结构,提高了代码阅读的难度。
二、模版方法模式的结构和实现
模版方法模式的结构需要父类和子类配合完成,父类定义通用的代码片段,如一个面板具体的绘制工作流程由父类来决定,制定一个详细的面板内容钩子方法,该方法需要子类来具体实现。所以通过继承创建的多个子类在绘画的过程中内容都是迥然不同的。
模板方法模式包含以下内容
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架(pipline)。它由一个模板方法和若干个基本方法构成,这些方法的定义如下。
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
基本方法:是整个算法中的一个步骤,包含以下几种类型。
1.抽象方法:在抽象类中申明,由具体子类实现。
2.具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
3.钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
三、模版方法的案例
public interface Drawing {
void draw();
}
/**
* 绘画中心
*/
public abstract class AbstractDrawingCenter implements Drawing {
/**
* 模版方法流程
*/
@Override
public void draw() {
Brush brush = createBrushInstance();
DrawPanel drawPanel = createDrawPanelInstance();
Pigment pigment = createPigmentInstance();
Painter painter = createPainterInstance();
painter.draw(brush, drawPanel, pigment);
}
/**
* 钩子方法子类自定义实现
*/
abstract public Brush createBrushInstance();
abstract public DrawPanel createDrawPanelInstance();
abstract public Pigment createPigmentInstance();
abstract public Painter createPainterInstance();
}
@Data
@AllArgsConstructor
public class Pigment {
private String name;
}
@Data
@AllArgsConstructor
public class Painter {
private static final Logger LOGGER = LoggerFactory.getLogger(Painter.class);
private String name;
public void draw(Brush brush, DrawPanel panel, Pigment pigment) {
LOGGER.info("画家【{}】绘画开始,使用【{},{},{}】这些工具开始他的创作之旅!", JSON.toJSONString(name), JSON.toJSONString(brush), JSON.toJSONString(panel), JSON.toJSONString(pigment));
}
}
@Data
@AllArgsConstructor
public class DrawPanel {
private String name;
}
@Data
@AllArgsConstructor
public class Brush {
private String name;
}
/**
* 自定义绘画中心,配置了专业的画家和各种不错的绘画工具。
*/
@Service("testDrawingCenter")
public class TestDrawingCenter extends AbstractDrawingCenter implements Drawing {
@Override
public Brush createBrushInstance() {
return new Brush("狼毫毛刷");
}
@Override
public DrawPanel createDrawPanelInstance() {
return new DrawPanel("宽大的画板");
}
@Override
public Pigment createPigmentInstance() {
return new Pigment("五彩缤纷的颜料");
}
@Override
public Painter createPainterInstance() {
return new Painter("专业的画家");
}
}
案例主要是有一个模版流程的方法draw(),该方法先获取画笔,画板,颜料,聘请画家进行绘画。定义的钩子方法可以让子类自定义实现,可扩展性很强,但是模版方法过深会影响可读性。