admin 管理员组

文章数量: 1184232

  1. 讲清“抽象类”和“接口”的本质区别
  2. 带你从零设计一个简单的“通知系统”
  3. 使用策略模式 + 接口 + 工厂模式,实现高扩展性架构

🎯 一、抽象类(abstract class) vs 接口(interface

对比项抽象类(abstract class接口(interface
关键字abstract classinterface
方法可以有抽象方法,也可以有具体实现JDK 8前:只能抽象方法
JDK 8+:可有default方法
成员变量可以有各种访问修饰符的变量只能是 public static final(常量)
构造器可以有构造器(用于初始化)不能有构造器
继承一个类只能继承一个抽象类一个类可以实现多个接口
关系语义“is-a” 关系(是什么)“can-do” 关系(能做什么)
设计目的共享代码 + 强制子类实现某些行为定义能力契约,支持多继承能力

🧠 核心理解:

比喻抽象类接口
汽车工厂所有车都有发动机、底盘(共性)
但跑车、卡车的“加速方式”不同
所有车都能“启动”、“刹车”(能力)
人类都有心跳、呼吸(抽象类提供默认实现)
但“工作方式”由子类决定
一个人可以“会游泳”、“会开车”、“会编程”(实现多个接口)

✅ 什么时候用抽象类?

当你有一组相似的类,它们共享代码逻辑,但某些行为由子类决定。

// 抽象类:所有支付方式都有“生成订单号”,但“支付逻辑”不同
public abstract class PaymentMethod {
    
    // 共享逻辑
    protected String generateOrderId() {
        return "PAY-" + System.currentTimeMillis();
    }

    // 强制子类实现
    public abstract boolean doPay(BigDecimal amount);
}

✅ 什么时候用接口?

当你想给类赋予某种能力,且一个类可以有多种能力。

public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

public class Duck implements Flyable, Swimmable {
    public void fly() { System.out.println("鸭子飞"); }
    public void swim() { System.out.println("鸭子游"); }
}

✅ 一个类可以“会飞”又“会游泳”,但不能“既是鸟又是鱼”(单继承限制)


🚨 企业级建议总结

场景推荐
需要共享代码(如日志、校验、初始化)✅ 抽象类
一个类有多种角色/能力✅ 接口
想让多个不相关的类都具备某行为✅ 接口
未来可能多继承能力✅ 接口
强调“是什么”(is-a)抽象类
强调“能做什么”(can-do)接口

💡 现代Java趋势:优先使用接口 + default方法,只有在需要共享状态或构造逻辑时才用抽象类。


🏗️ 二、设计“通知系统”—— 支持邮件、短信、APP推送

🎯 业务需求

我们有一个订单系统,用户下单后需要通知:

  • 老用户:发邮件 + APP推送
  • 新用户:发短信 + APP推送
  • VIP用户:只发APP推送(避免打扰)

我们要做到:
✅ 可扩展:未来加“微信通知”不改老代码
✅ 可组合:一个用户可触发多种通知
✅ 高内聚:每种通知独立实现


✅ 架构设计:策略模式(Strategy Pattern)


🔧 第一步:定义通知策略接口

// com.example.notification.strategy.NotificationStrategy.java
public interface NotificationStrategy {
    /**
     * 发送通知
     * @param userId 用户ID
     * @param message 消息内容
     */
    void send(Long userId, String message);
}

✅ 使用接口,因为“通知”是一种能力,不是“是什么”


🔧 第二步:实现具体通知方式

1. 邮件通知

// EmailNotification.java
@Component  // 交给Spring管理
@Slf4j
public class EmailNotification implements NotificationStrategy {

    @Override
    public void send(Long userId, String message) {
        log.info("发送邮件 | 用户ID: {} | 内容: {}", userId, message);
        // 调用邮件服务,如JavaMailSender
    }
}

2. 短信通知

// SmsNotification.java
@Component
@Slf4j
public class SmsNotification implements NotificationStrategy {

    @Override
    public void send(Long userId, String message) {
        log.info(" 发送短信 | 用户ID: {} | 内容: {}", userId, message);
        // 调用短信平台API
    }
}

3. APP推送

// AppPushNotification.java
@Component
@Slf4j
public class AppPushNotification implements NotificationStrategy {

    @Override
    public void send(Long userId, String message) {
        log.info(" APP推送 | 用户ID: {} | 内容: {}", userId, message);
        // 调用极光、个推等推送服务
    }
}

🔧 第三步:设计上下文(Context)—— 执行策略

// NotificationContext.java
@Component
public class NotificationContext {

    @Autowired
    private List<NotificationStrategy> strategies; // 自动注入所有实现类

    private final Map<UserType, List<NotificationStrategy>> strategyMap = new HashMap<>();

    // 初始化策略映射(可在配置中心动态加载)
    @PostConstruct
    public void init() {
        strategyMap.put(UserType.NEW, Arrays.asList(
            findStrategy(SmsNotification.class),
            findStrategy(AppPushNotification.class)
        ));
        strategyMap.put(UserType.NORMAL, Arrays.asList(
            findStrategy(EmailNotification.class),
            findStrategy(AppPushNotification.class)
        ));
        strategyMap.put(UserType.VIP, Collections.singletonList(
            findStrategy(AppPushNotification.class)
        ));
    }

    // 根据用户类型执行通知
    public void notify(User user, String message) {
        List<NotificationStrategy> userStrategies = strategyMap.get(user.getType());
        if (userStrategies != null) {
            userStrategies.forEach(strategy -> {
                try {
                    strategy.send(user.getId(), message);
                } catch (Exception e) {
                    log.error("通知发送失败 | 用户: {} | 策略: {}", user.getId(), strategy.getClass().getSimpleName(), e);
                }
            });
        }
    }

    // 辅助方法:从Spring容器中找具体实现
    private <T> T findStrategy(Class<T> clazz) {
        return strategies.stream()
                .filter(clazz::isInstance)
                .map(clazz::cast)
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("未找到策略实现: " + clazz));
    }
}

🔧 第四步:用户类型枚举

// UserType.java
public enum UserType {
    NEW,    // 新用户
    NORMAL, // 普通用户
    VIP     // VIP用户
}
java


深色版本
// User.java
@Data
public class User {
    private Long id;
    private String name;
    private UserType type;
}

🔧 第五步:控制器测试

// NotificationController.java
@RestController
@RequestMapping("/api/notify")
public class NotificationController {

    @Autowired
    private NotificationContext notificationContext;

    @PostMapping("/order-success")
    public ResponseEntity<String> orderSuccess(@RequestBody OrderSuccessDTO dto) {
        User user = userService.findById(dto.getUserId());
        String msg = "恭喜下单成功,订单号:" + dto.getOrderId();

        notificationContext.notify(user, msg);

        return ResponseEntity.ok("通知已发送");
    }
}

// DTO
@Data
public class OrderSuccessDTO {
    private Long userId;
    private String orderId;
}

✅ 测试效果

用户类型通知方式
新用户短信 + APP推送
普通用户邮件 + APP推送
VIP用户APP推送

优势

  • 新增“微信通知”?只需实现 NotificationStrategy,加到 strategyMap
  • 修改VIP用户策略?只改 init() 方法,不影响其他逻辑
  • 每种通知独立,便于单元测试和Mock

🚀 企业级扩展建议

1. 从配置中心加载策略(如 Nacos)

{
  "NEW": ["SMS", "APP_PUSH"],
  "NORMAL": ["EMAIL", "APP_PUSH"],
  "VIP": ["APP_PUSH"]
}

2. 异步执行通知(提升性能)

@Async
public void asyncSend(NotificationStrategy strategy, Long userId, String msg) {
    strategy.send(userId, msg);
}

3. 失败重试 + 告警

try {
    strategy.send(userId, msg);
} catch (Exception e) {
    retryService.scheduleRetry(userId, msg, strategy.getClass());
    alertService.send("通知失败", e);
}

4. 监控通知成功率

Metrics.counter("notification.sent", "type", "email").increment();

🎯 总结:你学到了什么?

技能说明
✅ 理解抽象类 vs 接口知道“is-a”和“can-do”的本质区别
✅ 掌握策略模式用接口 + 多态实现可扩展行为
✅ 设计高内聚低耦合系统每种通知独立,易于维护
✅ 使用Spring依赖注入自动管理策略实现
✅ 企业级可扩展设计支持动态配置、异步、监控

本文标签: 面向对象 实战 深度 java