模板方法模式
1. 概念
模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。
名词 | 说明 |
---|---|
算法 | 业务逻辑 |
算法骨架 | 模板 |
算法骨架的方法 | 模板方法 |
模板方法模式主要是用来解决复用和扩展两个问题。
-
复用:模板模式把一个算法中不变的流程抽象到父类的模板方法,将可变的部分封装成抽象方法交由子类去实现。对于不可变的部分得到了很好的复用。
-
扩展 框架通过模板模式提供功能扩展点,让框架用户可以在不修改框架源码的情况下,基于扩展点定制化框架的功能。
2. 角色职责
角色 | 职责 |
---|---|
AbstractClass | 抽象模板,不变的逻辑进行复用 |
ConcreteClass | 子类实现抽象模板的抽象方法,扩展点定制化框架的功能 |
3. 框架应用
模板方法在框架中最典型的应用应该就是Serverlet规范中的HttpServlet,如果抛开SpringMVC框架,我们需要实现一个web应用最简单的方式应该是继承HttpServlet,重写其中的doGet和doPost方法接着配置在web.xml配置该servlet。我们看看HttpServlet的service方法明显的模板方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
//省略get请求缓存相关代码
this.doGet(req, resp);
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
//省略
}
}
如上所示service服务根据不同的请求方式 调用doXXX()方法,doXXX方法都有有默认实现(避免子类继承后需要重写所有方法),所以子类去按需实现对应的doXXX方法。类似的模板方法还有InputStream的read方法等。
4. 回调和模板的区别
与模板模式相类似的还有回调方法准确的说是同步回调。异步回调更像是观察者模式的应用体现。先看一个回调示例
public interface CallBack {
//回调方法
void call();
}
public class BServer {
public void process(CallBack callBack){
//复用:除了callBack.call(); 其他代码均为可复用代码
System.out.println("BServer 服务相关逻辑开始");
//扩展 可以通过CallBack接口定义BServer的call方法
callBack.call();
System.out.println("BServer 服务相关逻辑结束");
}
}
回调跟模板模式一样,也具有复用和扩展的功能。除了回调函数之外,BServer类的 process() 函数中的逻辑都可以复用。同时我们可以通过 Callback 定制 process() 函数,也就是说,框架因此具有了扩展的能力。
异同
- 应用场景相同:同步回调跟模板模式几乎一致。它们都是在一个大的算法骨架中,自由替换其中的某个步骤,起到代码复用和扩展的目的
- 代码实现不同:回调和模板模式完全不同。回调基于组合关系来实现,模板模式基于继承,组合由于继承。所以更加灵活。
应用场景
Spring 提供了很多 Template 类,比如,JdbcTemplate、RedisTemplate、RestTemplate。尽管都叫作 xxxTemplate,但它们并非基于模板模式来实现的,而是基于回调来实现的。
策略模式
1. 概念
策略模式:定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端。
策略模式主要的作用还是解耦策略的定义、创建和使用,控制代码的复杂度,
让每个部分都不至于过于复杂、代码量过多。对于复杂代码策略模式还能
让其满足开闭原则,添加新策略的时候,最小化、集中化代码改动,减少引入bug的风险。
角色职责
角色 | 职责 |
---|---|
策略定义 | 策略类通常由接口定义和多个接口实现类 |
策略创建 | 策略实现类的创建逻辑一般基于工厂模式创建策略 |
策略使用 | 分为静态编译使用和动态运行,其中后者是典型的策略模式 |
当一个问题需要多种处理方式且这几种处理方式都仅仅是在具体行为上有差异时,我们可以将这个差异抽象出来作为一个统一的接口。然后,使用者在运行时就可以根据实际情况选择不同的接口实现来处理这个问题,这个接口实现可以理解为策略。用不同的策略来解决同一个或同一类问题,即策略模式。
2. 例子
以不同订单折扣策略为例,折扣有原价,半价,亏本三种策略,根据不同的类型获取不同的折扣策略。
策略定义
//订单折扣策略
public interface DiscountStrategy {
//折扣方法
void discount(Double price);
}
//相关实现
public class HalfDiscountStrategy implements DiscountStrategy{
@Override
public void discount(Double price) {
System.out.println("商品折扣50%,半价销售");
}
}
public class LoseDiscountStrategy implements DiscountStrategy{
@Override
public void discount(Double price) {
System.out.println("亏本大甩卖,赔钱销售");
}
}
策略使用
public void testClient(){
String type = "half";
DiscountStrategy discountStrategy = DisCountStrategyFactory.getDiscountStrategy(type);
discountStrategy.discount(5000.0D);
}
责任链模式
1. 概念
多个处理器依次处理同一个请求。形成一个链条。链条上的每个处理器各自承担各自的处理职责叫作职责链模式。
责任链有两种模式
- 同一个请求被所有的处理器处理。比如:一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器。
- 统一请求被其中一个处理器处理。比如:一个请求先经过 A 处理器处理A无法处理或者处理失败,然后再把请求传递给 B 处理器 B处理成功则不再往后传递。
角色 | 职责 |
---|---|
Handler | 处理器抽象方法 |
ConcreteHandler | 处理器具体实现 |
HandleChain | 处理器链,维护一份处理请求的处理链 |
Client | 客户端调用 |
作用
-
责任链为了解耦代码,应对代码的复杂性,让代码满足开闭原则,提高代码的可扩展性。
-
职责链模式常用在框架的开发中,为框架提供扩展点,让框架的使用者在不修改框架源码的情况下,基于扩展点添加新的功能。
2. 例子
/**
* 责任链处理模式
*/
public enum HandleMode{
//请求只需一个处理器进行处理成功即可
SINGLE("01", "单一处理模式"),
//请求需要所有处理器都进行处理
ALL("02", "全部处理模式");
}
- 以流水线包装产品为例来验证请求被所有处理器处理场景:某出口产品需要进行 分拣、塑封、打包等逻辑处理。
handle及实现
public abstract class Handler {
//持有下一个处理器
private Handler nextHandler;
//处理逻辑
public boolean handle() {
//TODO 通用的逻辑处理
boolean result = doHandle();
//如果为空则处理完成
if(nextHandler == null){
return true;