设计模式----模板模式
简述:
模板模式是一种抽象思维的具象实现,将多个子类的共性行为抽象到共有的父类实现,将子类的个性同样抽出抽象方法。父类定义一个final 修饰的模板方法,在模板方法中调用抽离的子类共性行为与子类的个性行为。简而言之模板方法的实现需要具备几个条件:
- 抽象的父类
- 父类中拥有子类个性化方法的抽象接口
- 父类拥有子类的共性方法的实现
- 父类定义了final 修饰的模板方法,并在模板方法中调用子类的个性化行为实现
示例:
抽象父类:
@Slf4j
public abstract class TestWorker<T> {
// fianl 实现的模板方法,其中调用子类的共性行为与个性行为
public final ProcessResult process(JobContext jobContext) throws Exception {
System.out.println("hello world!!");
System.err.println(jobContext.getJobParameters());
Map map = (Map)JSONObject.parse(jobContext.getJobParameters());
String procInstanceId = map.get("procInstanceId").toString();
String auditId = map.get("auditId").toString();
Data data = new Data();
data.setAuditId(auditId);
data.setProcInstanceId(procInstanceId);
openDoor((T) data);
startEngine();
gear((T) data);
go();
brake((T) data);
stop();
return new ProcessResult(InstanceStatus.SUCCESS, "\"hello world!!\"");
}
/**
*
*子类个性行为(理解:不同子类有不同的实现逻辑)
*/
abstract void openDoor(T t);
abstract void gear(T t);
abstract void brake(T t);
/**
*子类共性行为(简单理解为功能逻辑重复的代码)
*/
protected void startEngine() {
System.out.println("engine started !");
}
protected void go() {
System.out.println("running...");
}
protected void stop() {
System.out.println("stopped !");
}
}
子类实现:
- 宝马车:
@Component
public class BmwCar extends TestWorker<Data> {
@Override
public void openDoor(Data data) {
System.err.println("宝马车" + data.auditId + "打开了车门"); }
@Override
public void gear(Data data) {
System.err.println("宝马车" + data.auditId + "挂了1挡"); }
@Override
public void brake(Data data) {
System.err.println("宝马车" + data.auditId + "踩了刹车"); }
}
2.奔驰车:
@Componentpublic class BzCar extends TestWorker<Data> {
@Override public void openDoor(Data data) {
System.err.println("奔驰" + data.auditId + "打开了车门"); }
@Override public void gear(Data data) {
System.err.println("奔驰" + data.auditId + "挂了1挡"); }
@Override public void brake(Data data) {
System.err.println("奔驰" + data.auditId + "踩了刹车"); }
}
- 五菱车:
@Component
public class WulingCar extends TestWorker<Data> {
@Override
public void openDoor(Data data) {
System.err.println("五菱"+data.auditId+"打开了车门"); }
@Override
public void gear(Data data) {
System.err.println("五菱"+data.auditId+"挂了D挡"); }
@Override
public void brake(Data data) {
System.err.println("五菱"+data.auditId+"拉了手刹"); }
}
调用:
模板模式的使用充分体现了java语言的继承,封装与多态特性,子类继承父类抽离的共性行为,不在编写冗余代码,并同时优化了代码结构,模板模式下子类直接调用父类的模板方法,可实现父类的抽象的共有行为的共享与实现。
一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。
一个抽象类可以有任意多个模板方法,而不限于一个。每一个模板方法都可以调用任意多个具体方法。
基本方法又可以分为三种:抽象方法(Abstract Method)、具体方法(Concrete Method)和钩子方法(Hook Method)。
- 抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。
- 具体方法:一个具体方法由抽象类声明并实现,而子类并不实现或置换。
- 钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。
在上面的例子中,AbstractTemplate是一个抽象类,它带有三个方法。其中abstractMethod()是一个抽象方法,它由抽象类声明为抽象方法,并由子类实现;hookMethod()是一个钩子方法,它由抽象类声明并提供默认实现,并且由子类置换掉。concreteMethod()是一个具体方法,它由抽象类声明并实现。
默认钩子方法
一个钩子方法常常由抽象类给出一个空实现作为此方法的默认实现。这种空的钩子方法叫做“Do Nothing Hook”。显然,这种默认钩子方法在缺省适配模式里面已经见过了,一个缺省适配模式讲的是一个类为一个接口提供一个默认的空实现,从而使得缺省适配类的子类不必像实现接口那样必须给出所有方法的实现,因为通常一个具体类并不需要所有的方法。
命名规则
命名规则是设计师之间赖以沟通的管道之一,使用恰当的命名规则可以帮助不同设计师之间的沟通。
钩子方法的名字应当以do开始,这是熟悉设计模式的Java开发人员的标准做法。在上面的例子中,钩子方法hookMethod()应当以do开头;在HttpServlet类中,也遵从这一命名规则,如doGet()、doPost()等方法。
优缺点
模板方法模式的优点:
- 模板方法模式在一个类中形式化地定义算法,而由它的子类实现细节的处理。
- 模板方法是一种代码复用的基本技术。它们在类库中尤为重要,它们提取了类库中的公共行为。
- 模板方法模式导致一种反向的控制结构,这种结构有时被称为“好莱坞法则” ,即“别找我们,,我们找你”通过一个父类调用其子类的操作(而不是相反的子类调用父类),通过对子类的扩展增加新的行为,符合“开闭原则”
模板方法模式的缺点:
每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,但是更加符合“单一职责原则”,使得类的内聚性得以提高。
与其他模式的区别
1)策略模式:模板方法使用继承来改变算法的一部分。 Strategy使用委托来改变整个算法。模板方法模式与策略模式的作用十分类似,有时可以用策略模式替代模板方法模式。模板方法模式通过继承来实现代码复用,而策略模式使用委托,把不确定的行为集中到一个接口中,并在主类委托这个接口。委托比继承具有更大的灵活性。