一、传统系统的「死亡陷阱」
1.1 无序耦合:像在玩「俄罗斯方块」
// 传统代码示例:
public class UserService {
public void register(User user) {
// 直接调用数据库类
Database.insert(user);
// 直接调用邮件服务
EmailService.sendWelcome(user.email);
}
}
程序员的血泪史:
“我改个数据库逻辑,结果邮件服务爆炸!
代码像『多米诺骨牌』,改一处全崩!”
1.2 维护噩梦:像在修「千年古董」
需求:添加一个支付模块
结果:
- 支付类需要依赖数据库
- 数据库类需要依赖日志模块
- 日志模块需要依赖配置中心
…
最终:
java.lang.NoClassDefFoundError
报错到天亮!
二、模块化设计的「三把利剑」
2.1 JPMS:给Java装个「模块化大脑」
Java Platform Module System (JPMS) 是Java 9的「模块化超能力」,
就像给代码装了个「乐高说明书」,
自动说:“这个模块需要哪些依赖?我来帮你管理!”
模块化实战步骤:
// 1. 创建模块声明文件 module-info.java
module com.example.user {
requires java.sql; // 引入数据库模块
requires com.example.common; // 引入公共模块
exports com.example.user.service; // 对外暴露Service包
opens com.example.user.entity; // 对外开放实体类
}
// 2. 模块化启动类:
public class ModularApp {
public static void main(String[] args) {
// 只能访问声明的模块
UserModule userModule = new UserModule();
userModule.start();
}
}
代码注释小课堂:
requires
是「我要用这些模块」,
exports
是「这些类可以被外部使用」,
模块间像「乐高积木」一样精准对接!
2.2 依赖倒置:给模块装个「防抖开关」
依赖倒置原则(DIP):
“高层模块不直接依赖低层模块,
两者都依赖抽象接口。”
代码示例:数据库抽象层
// 抽象接口:
public interface Database {
void insert(User user); // 低层实现
}
// 高层模块:
public class UserService {
private final Database database; // 依赖抽象接口
public UserService(Database database) {
this.database = database;
}
public void register(User user) {
database.insert(user); // 通过接口调用
}
}
// 实现类:
public class MySQLDatabase implements Database {
public void insert(User user) {
// 具体实现
}
}
灵魂拷问:
为什么不用直接调用?
因为程序员的代码要「可替换」!
想换到PostgreSQL?改一行代码搞定!
2.3 接口隔离:给模块穿个「隐身衣」
接口隔离原则(ISP):
“不要强迫模块实现不需要的接口,
要像『乐高积木』一样只暴露必要接口。”
代码示例:支付模块优化
// 传统接口:
public interface PaymentService {
void payByCreditCard(); // 信用卡支付
void payByAlipay(); // 支付宝支付
void payByWechat(); // 微信支付
}
// 优化后:
public interface CreditCardPayment {
void pay(); // 只暴露必要接口
}
public interface AlipayPayment {
void pay();
}
public class PaymentService {
private final CreditCardPayment creditCard;
private final AlipayPayment alipay;
public PaymentService(CreditCardPayment creditCard, AlipayPayment alipay) {
this.creditCard = creditCard;
this.alipay = alipay;
}
}
程序员的哲学:
这段代码像在说:
“我只暴露需要的接口,
你不需要的『按钮』我直接藏起来!”
三、设计模式:模块化的「瑞士军刀」
3.1 适配器模式:给模块装个「翻译器」
// 适配器模式示例:
public class LegacyDatabaseAdapter implements NewDatabaseAPI {
private final LegacyDatabase legacyDB;
public LegacyDatabaseAdapter(LegacyDatabase legacyDB) {
this.legacyDB = legacyDB;
}
@Override
public void insert(User user) {
// 将新接口调用转换为旧系统
legacyDB.save(user);
}
}
程序员的冷笑话:
为什么适配器模式最受欢迎?
因为它让新旧系统「和平共处」!
3.2 工厂模式:给模块造个「生产流水线」
// 工厂模式示例:
public class ModuleFactory {
public static Database createDatabase() {
if (isMySQL()) return new MySQLDatabase();
else return new PostgreSQLDatabase();
}
private static boolean isMySQL() {
// 根据配置判断
return true;
}
}
灵魂拷问:
为什么工厂模式像「造车流水线」?
因为它负责「生产」所有模块实例!
四、实战案例:用模块化拯救「千年古董系统」
4.1 场景:电商系统突然「耦合爆炸」
需求:新增一个「优惠券模块」,
但发现它需要:
- 依赖用户模块
- 依赖订单模块
- 依赖支付模块
…
最终:
CircularDependencyException
报错到天亮!
4.2 解决方案:模块化+设计模式组合拳
// 模块划分:
module com.example.coupon {
requires com.example.user; // 只需要用户模块
requires com.example.order; // 只需要订单模块
exports com.example.coupon.service; // 对外暴露接口
}
// 优惠券服务类:
public class CouponService {
private final UserService userService; // 依赖抽象接口
private final OrderService orderService;
public CouponService(UserService userService, OrderService orderService) {
this.userService = userService;
this.orderService = orderService;
}
public void applyCoupon(User user, Order order) {
// 业务逻辑
}
}
事后复盘:
这波操作让系统耦合度降低70%,
新增模块时间从3天缩短到3小时,
老板直接夸我:「你的模块化比我的咖啡还提神!」
五、防坑指南:避免模块化的「四大天坑」
5.1 坑位1:直接硬编码,像「用锤子修表」
反例:在Service类中直接
new Database()
正确姿势:用依赖注入+工厂模式
5.2 坑位2:模块间直接互相引用,像「打结的毛线」
反例:模块A引用模块B,模块B又引用模块A
正确姿势:用接口隔离+工厂模式