如何用好莱坞原则优雅应对需求变更+面试官的灵魂暴击

87 阅读4分钟

5.2 模板方法


🚀 模式全景透视

设计哲学:好莱坞原则("别找我们,我们找你")的经典实践
杀伤力等级:⭐⭐⭐⭐⭐(高频面试模式Top3)
适用场景

  • 支付流程、工单审批等有固定流程的系统
  • 需要统一埋点/日志等横切关注点
  • 框架扩展点设计(如Spring JdbcTemplate)

🔍 模式解剖室(类图解析)

classDiagram
    class OrderProcessor {
        <<abstract>>
        +processOrder() final
        -validate()
        -deliver()
        +pay() abstract
        #needLog() boolean
        #logPayment()
    }
    
    class WechatOrderProcessor {
        +pay()
        +needLog() boolean
        +logPayment()
    }
    
    class AlipayOrderProcessor {
        +pay()
    }
    
    OrderProcessor <|-- WechatOrderProcessor
    OrderProcessor <|-- AlipayOrderProcessor

🧪 实战升级:电商订单

需求变更风暴
"要支持组合支付!国际站订单需要海关申报!虚拟商品无需物流!"

// 抽象模板增强版(应对复杂变更)
public abstract class OrderProcessor {
    public final void processOrder() {
        preCheck();
        validate();
        pay();
        postPayment();
        deliver();
        afterSales();
    }

    private void preCheck() {
        System.out.println("🔒 风控系统校验用户资质");
    }

    protected void validate() {
        System.out.println("✅ 基础库存校验");
    }

    // 新增海关申报扩展点
    protected void customsDeclaration() {}

    // 支付组合策略
    protected abstract void pay();

    private void postPayment() {
        System.out.println("📧 发送支付成功通知");
    }

    protected void deliver() {
        System.out.println("🚚 默认物流配送");
    }

    protected void afterSales() {
        System.out.println("📞 默认售后回访");
    }
}

// 国际站订单实现
public class InternationalOrder extends OrderProcessor {
    @Override
    protected void validate() {
        super.validate();
        System.out.println("🌍 校验跨境商品许可证");
    }

    @Override
    protected void pay() {
        System.out.println("💶 组合支付:信用卡+PayPal");
    }

    @Override
    protected void customsDeclaration() {
        System.out.println("🛃 生成海关电子报关单");
    }

    @Override
    protected void deliver() {
        System.out.println("✈️ 国际空运+清关服务");
    }
}

// 虚拟商品订单实现
public class VirtualOrder extends OrderProcessor {
    @Override
    protected void pay() {
        System.out.println("🔑 虚拟货币支付");
    }

    @Override
    protected void deliver() {
        System.out.println("📩 发送电子兑换码至邮箱");
    }

    @Override
    protected void afterSales() {
        // 禁用默认售后
    }
}

💣 设计模式地雷阵(常见错误)

  1. 过度抽象

    // 错误!将简单步骤拆分成多个抽象方法
    public abstract class OverEngineeredProcessor {
        public final void process() {
            step1();
            step2();
            step3();
        }
        abstract void step1();
        abstract void step2();
        abstract void step3();
    }
    

    修复方案:仅对真正需要变化的步骤进行抽象

  2. 忽略final关键字

    // 危险!允许子类重写模板方法
    public class ChaosProcessor extends OrderProcessor {
        public void processOrder() {
            // 完全破坏原有流程
        }
    }
    

    防御措施:模板方法必须用final修饰

  3. 滥用钩子方法

    // 错误示范:钩子方法变成主流程控制
    protected boolean shouldDeliver() {
        return globalConfig.get("deliverFlag");
    }
    

    最佳实践:钩子方法只用于可选扩展,不承担核心逻辑


🧠 高频面试题深度解析

1. 模板方法模式在Spring框架中的应用?

  • 答案示例
    JdbcTemplate是经典实现,其中execute()方法定义模板流程:
    1. 获取连接
    2. 创建语句
    3. 执行SQL(抽象方法)
    4. 处理结果(可覆盖)
    5. 关闭资源
    public class JdbcTemplate {
        public final Object query(String sql, RowMapper rowMapper) {
            // 模板方法流程...
        }
        
        protected Object extractData(ResultSet rs) {
            // 默认结果处理
        }
    }
    

2. 如何处理模板方法中的异常?

  • 策略建议
    ① 在抽象类中定义异常处理钩子方法
    ② 使用模板方法+策略模式组合
    public abstract class RobustProcessor {
        public final void execute() {
            try {
                doExecute();
            } catch (Exception e) {
                handleException(e);
            }
        }
        
        protected abstract void doExecute();
        
        protected void handleException(Exception e) {
            // 默认异常处理
        }
    }
    

3. 模板方法模式如何支持扩展开放/修改关闭原则?

  • 技术解析
    通过固定模板方法(关闭修改)和抽象方法(开放扩展)的组合实现。
    以Servlet生命周期为例:
    flowchart TD
        A[初始化init] --> B[处理请求service]
        B --> C[销毁destroy]
    
    开发者只需重写doGet/doPost,无需修改整体流程

4. 模板方法模式与建造者模式的区别?

  • 对比维度
    模板方法建造者模式
    控制方向父类控制流程指导者控制组装顺序
    扩展维度纵向扩展(子类实现步骤)横向扩展(不同建造者)
    典型应用框架流程定义复杂对象创建

🛠 重构实验室(实战演练)

原始代码(面条式代码):

public class PaymentService {
    public void wechatPay() {
        checkRisk();
        log.info("微信支付开始");
        // 微信支付逻辑
        log.info("微信支付完成");
        sendNotification();
    }
    
    public void aliPay() {
        checkRisk();
        log.info("支付宝支付开始");
        // 支付宝支付逻辑 
        log.info("支付宝支付完成");
        sendNotification();
    }
}

重构步骤

  1. 提取模板方法类AbstractPaymentProcessor
  2. 抽象支付核心方法
  3. 用钩子方法控制日志记录
  4. 最终代码:
public abstract class AbstractPaymentProcessor {
    public final void process() {
        checkRisk();
        doPayment();
        sendNotification();
    }
    
    private void checkRisk() {
        // 风控校验逻辑
    }
    
    protected abstract void doPayment();
    
    protected void sendNotification() {
        // 默认短信通知
    }
}

public class WechatPayment extends AbstractPaymentProcessor {
    @Override
    protected void doPayment() {
        // 微信支付具体实现
    }
    
    @Override
    protected void sendNotification() {
        // 微信服务通知
    }
}

📈 性能调优角斗场

场景:电商大促期间支付接口性能优化

模板方法优化策略

  1. 缓存优化:在抽象类中增加缓存检查钩子
    public abstract class CachedOrderProcessor extends OrderProcessor {
        @Override
        protected void validate() {
            if(checkCache()) return;
            super.validate();
        }
        
        protected abstract boolean checkCache();
    }
    
  2. 异步扩展:将非核心步骤改为异步执行
    public abstract class AsyncOrderProcessor extends OrderProcessor {
        @Override
        protected void deliver() {
            CompletableFuture.runAsync(super::deliver);
        }
    }
    
  3. 熔断保护:在模板方法中集成熔断机制
    public final void processOrder() {
        if(circuitBreaker.isOpen()) {
            fallback();
            return;
        }
        // 正常流程...
    }