admin 管理员组文章数量: 1184232
- 讲清“抽象类”和“接口”的本质区别
- 带你从零设计一个简单的“通知系统”
- 使用策略模式 + 接口 + 工厂模式,实现高扩展性架构
🎯 一、抽象类(abstract class) vs 接口(interface)
| 对比项 | 抽象类(abstract class) | 接口(interface) |
|---|---|---|
| 关键字 | abstract class | interface |
| 方法 | 可以有抽象方法,也可以有具体实现 | 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面向对象:实战演练 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1765978420a3428850.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论