在模板方法模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。例如,一个人每天会起床、吃饭、做事、睡觉等,其中“吃饭”和“做事”的内容每天可能不同。
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
模板方法模式包含以下主要角色:
(1) 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下:
① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
② 基本方法:是整个算法中的一个步骤,包含以下几种类型:
1.抽象方法:在抽象类中申明,由具体子类实现。
2.具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
3.钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
(2) 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
模板方法模式的结构图如图所示:
实现:
/**
*
* @ClassName: Shop
* @Description: 抽象模板角色
* @author: ljx
* @date: 2019年4月16日 上午10:51:18
*/
public abstract class Shop {
/**
*
* @Title: Template
* @Description: 模板方法:加上final 确保不会被子类修改
* @return: 计算的价钱
*/
public final double template(){
double price = price(10000);//假设该商品原价10000
double accountDiscount = accountDiscount();
return price * accountDiscount;
}
/**
*
* @Title: doGetShopType
* @Description: 基本方法(钩子方法)留给子类实现,得到客户类型
* @return
* @return: String
*/
protected String doGetShopType(){
return null;
}
/**
*
* @Title: accountDiscount
* @Description:基本方法(抽象方法)留给子类实现,计算折扣 不同类型的客户折扣不同
* @return
* @return: double
*/
protected abstract double accountDiscount();
/**
*
* @Title: price
* @Description: 基本方法(具体方法),已经实现,计算原价
* @param price
* @return
* @return: double
*/
protected double price(double price){
return price;
};
}
/**
*
* @ClassName: CommonClient
* @Description: 具体模板角色:普通客户
* @author: ljx
* @date: 2019年4月16日 上午11:02:23
*/
public class CommonClient extends Shop {
@Override
protected double accountDiscount() {
return 1.0;
}
}
/**
*
* @ClassName: MemberClicent
* @Description: 具体模板角色:会员客户
* @author: ljx
* @date: 2019年4月16日 上午11:08:45
*/
public class MemberClicent extends Shop {
@Override
public double accountDiscount() {
return 0.5;
}
@Override
public String doGetShopType(){
return "会员客户";
}
}
public class TemplateTeset {
public static void main(String[] args) {
Shop common = new CommonClient();
Shop menber = new MemberClicent();
System.out.println("客户类型:" + common.doGetShopType() + ",所需金额为:"+common.template());
System.out.println("客户类型:" + menber.doGetShopType() + ",所需金额为:"+menber.template());
}
}
结果为:
客户类型:null,所需金额为:10000.0
客户类型:会员客户,所需金额为:5000.0
... ...,欢迎指正!