从菜鸟到架构师:我与设计模式的爱恨情仇

举报
8181暴风雪 发表于 2025/08/29 19:39:15 2025/08/29
【摘要】 记得刚毕业那会儿,面试官问我什么是依赖注入,我支支吾吾半天说不清楚。现在回想起来,那时候的自己真是太天真了。经过这些年在不同项目中的摸爬滚打,我对这些概念有了更深的理解。今天就来聊聊我是如何在实战中真正理解并应用这些设计理念的。 一、依赖注入:从手动挡到自动挡的进化依赖注入(Dependency Injection)这个概念,说简单也简单,说复杂也复杂。刚开始写代码的时候,我经常写出这样的代...

记得刚毕业那会儿,面试官问我什么是依赖注入,我支支吾吾半天说不清楚。现在回想起来,那时候的自己真是太天真了。经过这些年在不同项目中的摸爬滚打,我对这些概念有了更深的理解。今天就来聊聊我是如何在实战中真正理解并应用这些设计理念的。

一、依赖注入:从手动挡到自动挡的进化

依赖注入(Dependency Injection)这个概念,说简单也简单,说复杂也复杂。刚开始写代码的时候,我经常写出这样的代码:

public class OrderService {
    private UserDao userDao = new UserDao();
    private ProductDao productDao = new ProductDao();
    private PaymentService paymentService = new PaymentService();
    
    public void createOrder(Long userId, Long productId) {
        // 业务逻辑
    }
}

每次需要修改数据库实现或者写单元测试的时候,都要改一大堆代码。后来终于明白了,这就是典型的高耦合!

1.1 依赖注入的演进历程

我经历过的依赖注入方式演进:

阶段 实现方式 优点 缺点 适用场景
原始阶段 直接new对象 简单直接 耦合度高,难以测试 小型demo
工厂模式 工厂类创建对象 一定程度解耦 工厂类本身复杂 中小型项目
手动注入 构造函数/setter注入 灵活可控 繁琐,容易出错 特定场景
容器注入 Spring/Guice等框架 自动化程度高 学习成本,运行开销 企业级应用
编译期注入 Dagger2 性能好,编译期检查 学习曲线陡峭 性能敏感场景

1.2 一个真实的重构案例

去年接手了一个老项目,里面的依赖关系错综复杂,改一个地方要修改十几个文件。我们决定引入依赖注入进行重构。

重构前的痛点:

  • 单元测试几乎不可能,因为依赖太多真实组件
  • 想切换缓存实现(从Ehcache到Redis),需要修改30多个类
  • 新人接手代码,完全搞不清楚依赖关系

重构后的代码结构:

@Service
public class OrderService {
    private final UserRepository userRepository;
    private final ProductRepository productRepository;
    private final PaymentService paymentService;
    
    @Autowired
    public OrderService(UserRepository userRepository, 
                       ProductRepository productRepository,
                       PaymentService paymentService) {
        this.userRepository = userRepository;
        this.productRepository = productRepository;
        this.paymentService = paymentService;
    }
}

重构带来的好处立竿见影:

  1. 单元测试覆盖率从5%提升到了85%
  2. 切换技术栈只需要修改配置类
  3. 代码可读性大幅提升

二、多态性:写出优雅代码的基石

多态性(Polymorphism)是面向对象的精髓之一。但说实话,真正理解它的威力是在一次支付系统的开发中。

2.1 没有多态的日子

最开始,我们的支付代码是这样的:

public void processPayment(String paymentType, Order order) {
    if ("alipay".equals(paymentType)) {
        // 支付宝支付逻辑
    } else if ("wechat".equals(paymentType)) {
        // 微信支付逻辑
    } else if ("unionpay".equals(paymentType)) {
        // 银联支付逻辑
    }
    // 每加一种支付方式,就要改这个方法
}

这种代码的问题显而易见:违反开闭原则,可维护性差,容易出bug。

2.2 多态的实际应用

重构后,我们使用了策略模式结合多态:

支付方式 实现类 特殊处理 回调机制
支付宝 AlipayStrategy 异步通知 HTTP回调
微信支付 WechatPayStrategy 签名验证 XML格式
银联 UnionPayStrategy 加密传输 同步返回
PayPal PayPalStrategy 货币转换 Webhook

通过多态,每种支付方式都有自己的实现:

public interface PaymentStrategy {
    PaymentResult pay(Order order);
    void handleCallback(String callbackData);
}

@Component
public class PaymentContext {
    private Map<String, PaymentStrategy> strategies;
    
    public PaymentResult executePayment(String type, Order order) {
        return strategies.get(type).pay(order);
    }
}

这样的设计让我们在后续添加Apple Pay、Google Pay等新支付方式时,完全不需要修改核心代码。

三、单例模式:用对了是蜜糖,用错了是砒霜

单例模式(Singleton Pattern)可能是最被滥用的设计模式了。我曾经也是"单例模式爱好者",直到被坑了几次才明白,不是所有场景都适合用单例。

3.1 单例模式的各种实现

这些年用过的单例实现方式:

实现方式 线程安全 性能 延迟加载 推荐指数
懒汉式 ★★
同步方法 ★★
双重检查锁 ★★★
静态内部类 ★★★★
枚举 ★★★★★

3.2 单例模式的坑

分享一个血泪教训。有一次,我们用单例模式实现了一个配置管理器:

public class ConfigManager {
    private static ConfigManager instance;
    private Properties config = new Properties();
    
    private ConfigManager() {
        loadConfig();
    }
    
    public static synchronized ConfigManager getInstance() {
        if (instance == null) {
            instance = new ConfigManager();
        }
        return instance;
    }
}

上线后发现了严重问题:

  1. 在分布式环境下,每个JVM都有自己的单例实例
  2. 配置更新后,需要重启才能生效
  3. 单元测试时,不同测试用例会相互影响

3.3 正确使用单例的场景

后来我总结了适合使用单例的场景:

  • 无状态的工具类
  • 线程池、连接池等资源管理器
  • 日志对象
  • 设备驱动程序

而不适合的场景:

  • 需要在集群环境共享的对象
  • 包含大量可变状态的对象
  • 需要继承的类

四、面向切面编程:横切关注点的优雇解决方案

面向切面编程(Aspect-Oriented Programming, AOP)是我最晚接触但最快爱上的技术。它解决了很多让人头疼的横切关注点问题。

4.1 AOP的实战场景

在实际项目中,我用AOP解决过这些问题:

应用场景 切面实现 解决的问题 效果
日志记录 @LogAspect 业务代码混杂日志 代码清晰度提升80%
性能监控 @PerformanceMonitor 手动计时繁琐易漏 自动化监控覆盖率100%
权限控制 @SecurityCheck 权限检查代码重复 减少90%重复代码
事务管理 @Transactional 手动事务易出错 事务一致性保证
缓存处理 @Cacheable 缓存逻辑侵入业务 缓存命中率提升50%
参数校验 @ValidateParams 校验代码冗余 统一校验规范

4.2 一个AOP改造的实例

我们有个老系统,每个方法都要记录执行时间和参数,代码是这样的:

public User getUserById(Long id) {
    long start = System.currentTimeMillis();
    logger.info("getUserById called with id: " + id);
    try {
        User user = userDao.findById(id);
        logger.info("getUserById executed in " + 
            (System.currentTimeMillis() - start) + "ms");
        return user;
    } catch (Exception e) {
        logger.error("getUserById failed", e);
        throw e;
    }
}

每个方法都是这样,维护起来简直是噩梦!

使用AOP改造后:

@Aspect
@Component
public class MethodLoggingAspect {
    
    @Around("@annotation(Loggable)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        logger.info("{} called with args: {}", methodName, Arrays.toString(args));
        
        try {
            Object result = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - start;
            logger.info("{} executed in {} ms", methodName, executionTime);
            return result;
        } catch (Exception e) {
            logger.error("{} failed", methodName, e);
            throw e;
        }
    }
}

// 业务代码变得无比清爽
@Loggable
public User getUserById(Long id) {
    return userDao.findById(id);
}

4.3 AOP的注意事项

使用AOP也踩过不少坑:

  1. 性能开销:过度使用AOP会带来性能损耗,特别是在高频调用的方法上
  2. 调试困难:AOP会改变代码执行流程,调试时可能会困惑
  3. 代理限制:Spring AOP基于代理,对final方法、私有方法无效
  4. 执行顺序:多个切面的执行顺序需要特别注意

五、设计模式综合实践

经过这些年的实践,我对设计模式有了一些自己的理解:

5.1 设计模式不是银弹

很多人学了设计模式后,恨不得在每个地方都用上。我也经历过这个阶段,结果代码变得过度设计,简单问题复杂化。

5.2 组合使用效果更好

实际项目中,往往需要多个模式配合使用:

  • 依赖注入 + 策略模式 = 灵活的业务策略切换
  • 单例模式 + 工厂模式 = 资源的统一管理
  • AOP + 注解 = 优雅的横切关注点处理

5.3 持续学习和实践

技术在不断演进,设计模式的应用也在变化。比如函数式编程的兴起,让某些传统设计模式有了新的实现方式。

最后想说的是,设计模式只是工具,真正重要的是解决问题的思路。不要为了用设计模式而用设计模式,而是要在合适的场景选择合适的方案。希望我的这些经验能给大家一些参考,少走一些弯路。

你在使用这些设计模式时有什么心得体会?欢迎在评论区分享交流!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。