一 模式背景
有时候,我们可能会遇到这么一种情况,不同的场景下,执行的业务算法的流程或者是骨架是一样的,但是在这些算法中,有一些是可以复用的,而且扩展起来也不是很方便。
二 模板方法模式
2.1 什么是模板方法模式
模板方法模式:固定算法骨架(流程),将一些步骤延迟到子类实现。这样既方便扩展,而且我们可以将一些公共的算法、提取出来。
2.2 优缺点
优点:
1 可以封装不变部分,扩展可变部分
2 提取出公共部分代码,便于维护,也避免代码重复
缺点:
算法骨架如果要修改,则子类都会修改,所以需要尽量是以后不会变的固定算法
2.3 使用场景
1 重构的时候,模板方法是常见的手段
2 多个类有着大致相同的逻辑时,只是有些具体实现不一样,可以使用
四 代码示例
public abstract class AbstractTemplate {
public abstract boolean validateParams(Integer itemId);
public <T> T queryItem(Class clazz, Integer itemId) {
return (T)Repository.queryItem(clazz,itemId);
}
public abstract <T> String buildSeoLink(T item);
public <T> String buildSeoURL(Class<T> clazz, Integer itemId) {
boolean valid = validateParams(itemId);
if (!valid) {
throw new IllegalArgumentException("[buildSeoURL] 提供的参数非法,请检查");
}
T item = queryItem(clazz, itemId);
if (item == null) {
return null;
}
return buildSeoLink(item);
}
}
public class CategorySeoURLTemplate extends AbstractTemplate {
public boolean validateParams(Integer itemId) {
return itemId < 1000;
}
public <T> String buildSeoLink(T item) {
if (item == null) {
return null;
}
Category category = (Category)item;
StringBuilder sb = new StringBuilder();
if (category.getParent() != null) {
String parentSeoLink = buildSeoLink(category.getParent());
sb.append(parentSeoLink);
}
String displayName = category.getDisplayName();
String seoLink = StringUtils.formatSeoURL(displayName,' ','-',Boolean.TRUE);
sb.append("/").append(seoLink);
return sb.toString();
}
}
public class ProductSeoURLTemplate extends AbstractTemplate {
public boolean validateParams(Integer itemId) {
return itemId > 1000 || itemId < 10000;
}
public <T> String buildSeoLink(T item) {
if (item == null) {
return null;
}
Product product = (Product)item;
StringBuilder sb = new StringBuilder();
String displayName = product.getDisplayName();
String seoLink = StringUtils.formatSeoURL(displayName,' ','-',Boolean.TRUE);
sb
.append("/")
.append(seoLink)
.append("?pid=")
.append(product.getProductId());
return sb.toString();
}
}
public class SKUSeoURLTemplate extends AbstractTemplate {
public boolean validateParams(Integer itemId) {
return itemId > 100000 || itemId < 100000;
}
public <T> String buildSeoLink(T item) {
if (item == null) {
return null;
}
SKU sku = (SKU)item;
StringBuilder sb = new StringBuilder();
String seoName = sku.getSeoName();
String seoLink = StringUtils.formatSeoURL(seoName,' ','-',Boolean.TRUE);
sb.append("/").append(seoLink);
sb.append("-")
.append(sku.getProduct()
.getProductId())
.append("?sid=")
.append(sku.getSkuId());
return sb.toString();
}
}
public class Repository<T> {
private static Map<Integer,Category> categoryDB = new HashMap<Integer, Category>();
private static Map<Integer,Product> productDB = new HashMap<Integer, Product>();
private static Map<Integer,SKU> skuDB = new HashMap<Integer, SKU>();
static {
Category c1 = new Category(1,"Household Electrical Appliances",null);
Category c12 = new Category(12,"Television",c1);
Category c121 = new Category(121,"Smart Television",c12);
Category c2 = new Category(2,"Office",null);
Category c21 = new Category(21,"Computer",c2);
Category c211 = new Category(211,"Laptop",c21);
categoryDB.put(c1.getCategoryId(),c1);
categoryDB.put(c12.getCategoryId(),c12);
categoryDB.put(c121.getCategoryId(),c121);
categoryDB.put(c2.getCategoryId(),c2);
categoryDB.put(c21.getCategoryId(),c21);
categoryDB.put(c211.getCategoryId(),c211);
Product p1 = new Product(1000,"Sunglasses");
Product p2 = new Product(2000,"Premium Aviator Sunglasses");
productDB.put(p1.getProductId(),p1);
productDB.put(p2.getProductId(),p1);
SKU s1 = new SKU(100001,"Red Sunglasses","sunglassess red",p1);
SKU s2 = new SKU(100002,"Black Sunglasses","sunglassess black",p1);
SKU s3 = new SKU(200010,"Men Premium Aviator Sunglasses","men premium aviator sunglasses",p2);
SKU s4 = new SKU(200012,"Women Premium Aviator Sunglasses","women premium aviator sunglasses black",p2);
skuDB.put(s1.getSkuId(),s1);
skuDB.put(s2.getSkuId(),s2);
skuDB.put(s3.getSkuId(),s3);
skuDB.put(s4.getSkuId(),s4);
}
public static Object queryItem(Class clazz, Integer itemId) {
if (clazz == Category.class) {
return categoryDB.get(itemId);
} else if (clazz == Product.class) {
return productDB.get(itemId);
} else if (clazz == SKU.class) {
return skuDB.get(itemId);
}
return null;
}
}
public class StringUtils {
public static boolean isBlank(String str) {
return str == null || str.trim().equals("");
}
public static boolean isNotBlank(String str) {
return str != null && !str.trim().equals("");
}
public static boolean isAlphanumericSpace(char ch) {
return Character.isLetterOrDigit(ch) || (ch == ' ');
}
public static final String formatSeoURL(String pSrcLink, char pSrcDelim, char pDestDelim, boolean toLowerCase){
if (isBlank(pSrcLink)) {
return null;
}
if (pSrcLink.indexOf("&") != -1) {
pSrcLink = pSrcLink.replaceAll("&", "and");
}
char[] chars = pSrcLink.trim().toCharArray();
for(int i = 0; i < chars.length; i++){
if(chars[i] == '\'' || chars[i] == '-'){
continue;
}
if(isAlphanumericSpace(chars[i])){
continue;
}
pSrcLink = remove(pSrcLink, chars[i]);
}
if(toLowerCase){
pSrcLink = pSrcLink.toLowerCase();
}
pSrcLink = pSrcLink.replaceAll("\\s{1,}", " ");
pSrcLink = pSrcLink.replace(pSrcDelim, pDestDelim);
return pSrcLink;
}
public static String remove(String str, char remove) {
if ((isBlank(str)) || (str.indexOf(remove) == -1)) {
return str;
}
char[] chars = str.toCharArray();
int pos = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] != remove) {
chars[(pos++)] = chars[i];
}
}
return new String(chars, 0, pos);
}
}
public class Client {
public static void main(String[] args) {
AbstractTemplate template = new CategorySeoURLTemplate();
String catSeoURL = template.buildSeoURL(Category.class, 121);
System.out.println(catSeoURL);
// 切换模板
template = new ProductSeoURLTemplate();
String prodSeoURL = template.buildSeoURL(Product.class, 2000);
System.out.println(prodSeoURL);
template = new SKUSeoURLTemplate();
String skuSeoURL = template.buildSeoURL(SKU.class, 200012);
System.out.println(skuSeoURL);
}
}