工作中用到过哪些设计模式?是怎么实现的?

发布于:2025-07-04 ⋅ 阅读:(19) ⋅ 点赞:(0)

1. 单例模式(结合 Spring @Component)

场景:配置中心、全局状态管理
Spring 实现

java

// 自动注册为Spring Bean(默认单例)
@Component
public class AppConfig {
    @Value("${server.port}")
    private int port;
    
    // 其他配置属性和方法
}

// 使用方式(依赖注入)
@Service
public class UserService {
    @Autowired
    private AppConfig appConfig;
    
    public void startServer() {
        System.out.println("启动服务器,端口:" + appConfig.getPort());
    }
}

面试话术
“在微服务项目中,我们用@Component注解将配置类声明为 Spring Bean,默认就是单例模式。比如 AppConfig 类统一管理应用配置,通过@Value注入配置值,所有需要使用配置的服务直接@Autowired即可。Spring 容器帮我们保证了全局唯一实例,无需手动实现双重检查锁,代码更简洁。”

2. 工厂模式(结合 Spring FactoryBean)

场景:动态创建不同类型的数据源、Feign 客户端
Spring 实现

java

// 数据源工厂(简化示例)
@Configuration
public class DataSourceConfig {
    @Bean
    public FactoryBean<DataSource> dataSourceFactory() {
        return new DynamicDataSourceFactory();
    }
}

// 自定义FactoryBean
public class DynamicDataSourceFactory implements FactoryBean<DataSource> {
    @Override
    public DataSource getObject() {
        // 根据条件创建不同数据源(如主从库)
        return DataSourceBuilder.create()
                .url("jdbc:mysql://localhost:3306/mydb")
                .username("root")
                .password("password")
                .build();
    }
    
    @Override
    public Class<?> getObjectType() {
        return DataSource.class;
    }
}

// 使用方式
@Service
public class DataService {
    @Autowired
    private DataSource dataSource;
}

面试话术
“在分库分表场景中,我们用FactoryBean接口实现动态数据源创建。比如根据租户 ID 选择主库或从库,通过自定义DynamicDataSourceFactory,Spring 会自动调用getObject()方法获取数据源实例。这种方式比传统工厂模式更优雅,与 Spring 的依赖注入体系无缝集成。”

3. 策略模式(结合 @Autowired+Map)

场景:支付方式、限流策略选择
Spring 实现

java

// 支付策略接口
public interface PaymentStrategy {
    void pay(BigDecimal amount);
}

// 支付宝策略
@Service("alipay")
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付:" + amount);
    }
}

// 微信支付策略
@Service("wechatpay")
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("微信支付:" + amount);
    }
}

// 策略上下文(自动注入所有实现类)
@Service
public class PaymentService {
    @Autowired
    private Map<String, PaymentStrategy> strategyMap;
    
    public void processPayment(String paymentType, BigDecimal amount) {
        PaymentStrategy strategy = strategyMap.get(paymentType);
        if (strategy != null) {
            strategy.pay(amount);
        } else {
            throw new IllegalArgumentException("不支持的支付类型");
        }
    }
}

面试话术
“在支付模块中,我们用策略模式结合 Spring 的自动注入特性。每种支付方式实现PaymentStrategy接口并标记@Service,通过@Autowired Map<String, PaymentStrategy>可以自动收集所有策略实现类,Key 为 Bean 名称(如alipay)。这种方式避免了传统策略模式中的大量if-else,新增支付方式只需添加实现类,无需修改原有代码。”

4. 观察者模式(结合 Spring 事件机制)

场景:订单状态变更通知、异步事件处理
Spring 实现

java

// 定义事件
public class OrderPaidEvent extends ApplicationEvent {
    private final String orderId;
    
    public OrderPaidEvent(Object source, String orderId) {
        super(source);
        this.orderId = orderId;
    }
    
    public String getOrderId() {
        return orderId;
    }
}

// 发布事件
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void payOrder(String orderId) {
        // 处理支付逻辑
        eventPublisher.publishEvent(new OrderPaidEvent(this, orderId));
    }
}

// 监听事件(方式1:实现ApplicationListener)
@Component
public class SmsNotificationListener implements ApplicationListener<OrderPaidEvent> {
    @Override
    public void onApplicationEvent(OrderPaidEvent event) {
        System.out.println("发送短信通知:订单" + event.getOrderId() + "已支付");
    }
}

// 监听事件(方式2:@EventListener注解)
@Component
public class LoggingListener {
    @EventListener
    public void handleOrderPaid(OrderPaidEvent event) {
        System.out.println("记录日志:订单" + event.getOrderId() + "已支付");
    }
}

面试话术
“在订单系统中,我们用 Spring 的事件机制替代传统观察者模式。当订单支付成功后,通过ApplicationEventPublisher发布OrderPaidEvent,短信通知和日志记录组件分别用@EventListener注解监听该事件。这种方式比手动维护观察者列表更灵活,支持异步处理(通过@Async注解),而且与 Spring 生态深度集成,例如可以结合事务实现事件的可靠发布。”

5. 装饰器模式(结合 Spring AOP)

场景:权限校验、日志增强、缓存切面
Spring 实现

java

// 定义切面
@Aspect
@Component
public class LoggingAspect {
    @Around("execution(* com.example.service.*.*(..))")
    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("方法开始:" + joinPoint.getSignature().getName());
        long startTime = System.currentTimeMillis();
        
        Object result = joinPoint.proceed(); // 执行原方法
        
        long endTime = System.currentTimeMillis();
        System.out.println("方法结束:" + joinPoint.getSignature().getName() + 
                          ",耗时:" + (endTime - startTime) + "ms");
        return result;
    }
}

面试话术
“在性能监控场景中,我们用 Spring AOP 实现装饰器模式。通过@Around注解定义切面,在方法执行前后添加日志记录和耗时统计逻辑,无需修改原有业务代码。这种方式比传统继承或组合更灵活,可以动态控制哪些方法需要增强,而且支持切入点表达式精确匹配目标方法。”

面试应答技巧

  1. 突出框架整合:强调如何利用 Spring 特性(如依赖注入、事件机制、AOP)简化设计模式实现,而非手动编码。
  2. 结合业务场景:举例说明在微服务、分布式系统中的应用(如 Feign 客户端工厂、Sentinel 限流策略)。
  3. 对比传统方式:指出相比 “手搓” 设计模式,Spring 提供的方案更优雅、更符合开闭原则。

例如:

“在我们的微服务项目中,服务间通信使用 Feign 客户端。当需要根据环境动态切换服务 URL 时,我们实现了FeignClientFactoryBean,利用 Spring 的工厂机制和配置中心(Nacos),根据不同环境加载不同的服务地址。这种方式比硬编码 URL 更灵活,符合工厂模式的思想,同时充分利用了 Spring 的生态优势。”


网站公告

今日签到

点亮在社区的每一天
去签到