完整项目实战(三):代码实现——核心模块编码与集成
好,到了这一步,前面那些设计图、类图、接口定义,终于要落地成代码了。说实话,每次到这个阶段,我反而会松一口气——因为抽象的东西已经想透了,剩下的就是敲键盘的体力活。但别大意,编码阶段最容易出幺蛾子,尤其是模块之间的集成。
今天我们就来把单例、工厂、观察者这三个模式,真正揉进一个项目里。我会带着你一步步写核心代码,然后看它们怎么拼到一起。
项目背景回顾
我们做一个简单的日志监控系统。需求是这样的:
- 系统里只有一个日志管理器(单例模式)
- 根据配置创建不同的日志处理器(工厂模式)
- 当日志级别达到告警阈值时,通知所有订阅者(观察者模式)
说白了,就是一个能写日志、能报警、还能灵活切换输出方式的小框架。我在实际项目中遇到过类似的需求,当时客户要求日志既要写文件,又要发邮件,还要推送到消息队列——嗯,观察者模式就是为这种场景量身定做的。
第一步:单例模式——日志管理器的实现
先写最核心的 LoggerManager。这个类全局只有一个实例,所有模块都通过它来记录日志。
public class LoggerManager {
private static volatile LoggerManager instance;
private List<LogHandler> handlers;
private List<AlertObserver> observers;
private LoggerManager() {
handlers = new ArrayList<>();
observers = new ArrayList<>();
System.out.println("[LoggerManager] 初始化完成");
}
public static LoggerManager getInstance() {
if (instance == null) {
synchronized (LoggerManager.class) {
if (instance == null) {
instance = new LoggerManager();
}
}
}
return instance;
}
public void log(String level, String message) {
for (LogHandler handler : handlers) {
handler.handle(level, message);
}
// 如果级别是 ERROR,触发观察者通知
if ("ERROR".equals(level)) {
notifyObservers(message);
}
}
public void addHandler(LogHandler handler) {
handlers.add(handler);
}
public void addObserver(AlertObserver observer) {
observers.add(observer);
}
private void notifyObservers(String message) {
for (AlertObserver observer : observers) {
observer.onAlert(message);
}
}
}
这里用了双重检查锁定的方式。为什么?因为既要保证线程安全,又不能每次调用都加锁影响性能。我早期写过一段代码,直接用 synchronized 修饰整个方法,结果在高并发下性能惨不忍睹。后来改成这种写法,才算是两全其美。
第二步:工厂模式——日志处理器的创建
接下来是工厂。我们需要根据配置字符串,创建不同的 LogHandler 实现类。比如 "file" 对应文件日志,"console" 对应控制台输出。
public interface LogHandler {
void handle(String level, String message);
}
public class FileLogHandler implements LogHandler {
@Override
public void handle(String level, String message) {
System.out.println("[文件日志] " + level + ": " + message);
// 实际项目中这里会写文件
}
}
public class ConsoleLogHandler implements LogHandler {
@Override
public void handle(String level, String message) {
System.out.println("[控制台] " + level + ": " + message);
}
}
public class LogHandlerFactory {
public static LogHandler createHandler(String type) {
switch (type.toLowerCase()) {
case "file":
return new FileLogHandler();
case "console":
return new ConsoleLogHandler();
default:
throw new IllegalArgumentException("未知的日志处理器类型: " + type);
}
}
}
工厂模式的好处是什么?你想想看,如果以后要加一个 DatabaseLogHandler,只需要新增一个类,然后在工厂里加一个 case 分支就行。调用方完全不用改代码。我在项目中用这个模式重构过一个支付渠道对接的模块,原来每次加新渠道都要改十几个文件,改成工厂之后,只改一个地方。
第三步:观察者模式——告警通知机制
最后是观察者。当日志级别为 ERROR 时,所有注册的观察者都会收到通知。比如发邮件、发短信、或者弹窗。
public interface AlertObserver {
void onAlert(String message);
}
public class EmailAlertObserver implements AlertObserver {
private String email;
public EmailAlertObserver(String email) {
this.email = email;
}
@Override
public void onAlert(String message) {
System.out.println("[邮件告警] 发送至 " + email + ": " + message);
}
}
public class SmsAlertObserver implements AlertObserver {
private String phone;
public SmsAlertObserver(String phone) {
this.phone = phone;
}
@Override
public void onAlert(String message) {
System.out.println("[短信告警] 发送至 " + phone + ": " + message);
}
}
观察者模式的核心思想就是“发布-订阅”。日志管理器是发布者,各种告警器是订阅者。两者之间通过接口解耦。我做过一个实时风控系统,里面用了类似的模式——交易异常时,同时通知风控审核、用户、以及运维团队。如果不用观察者,那代码里就得写一堆 if-else 调用,想想就头疼。
第四步:模块集成——让三个模式协同工作
现在三个核心模块都写好了,怎么把它们串起来?我们写一个 Main 类来演示集成过程。
public class Main {
public static void main(String[] args) {
// 1. 获取单例
LoggerManager logger = LoggerManager.getInstance();
// 2. 通过工厂创建处理器
LogHandler fileHandler = LogHandlerFactory.createHandler("file");
LogHandler consoleHandler = LogHandlerFactory.createHandler("console");
// 3. 注册处理器
logger.addHandler(fileHandler);
logger.addHandler(consoleHandler);
// 4. 注册观察者
logger.addObserver(new EmailAlertObserver("admin@example.com"));
logger.addObserver(new SmsAlertObserver("13800138000"));
// 5. 模拟日志输出
logger.log("INFO", "系统启动成功");
logger.log("WARN", "磁盘空间不足");
logger.log("ERROR", "数据库连接超时");
}
}
运行结果应该是这样的:
[LoggerManager] 初始化完成
[文件日志] INFO: 系统启动成功
[控制台] INFO: 系统启动成功
[文件日志] WARN: 磁盘空间不足
[控制台] WARN: 磁盘空间不足
[文件日志] ERROR: 数据库连接超时
[控制台] ERROR: 数据库连接超时
[邮件告警] 发送至 admin@example.com: 数据库连接超时
[短信告警] 发送至 13800138000: 数据库连接超时
你看,INFO 和 WARN 级别的日志只走了处理器,而 ERROR 级别的日志除了写文件和控制台,还触发了邮件和短信告警。这就是三个模式协同工作的效果。
核心模块关系图
下面这张图展示了三个模式在项目中的协作关系。我特意把箭头画得清晰一些,方便你理解数据流向。
集成时容易踩的坑
代码写完了,但集成的时候有几个地方特别容易出问题。我把自己踩过的坑列出来,你注意避开。
- 单例被多次初始化:如果用了序列化或者反射,单例可能会被破坏。解决方案是在
readResolve()方法里返回同一个实例,或者直接用枚举实现单例。 - 工厂的扩展性:如果处理器类型很多,switch 会变得臃肿。我建议用注册表的方式,把类型和实现类的映射关系放在一个 Map 里,通过配置文件动态加载。
- 观察者的内存泄漏:如果观察者注册后没有及时注销,会导致内存泄漏。尤其是在 Web 应用中,每次请求都注册一个新观察者,最后内存就爆了。记得在不需要时调用
removeObserver()。
好了,这一章的核心代码实现就到这里。代码量不大,但每个模式都承担了明确的职责。你可以在自己的项目里试着搭一套类似的框架,把日志、告警、配置管理这些通用功能抽象出来。相信我,以后维护起来会轻松很多。
公众号:蓝海资料掘金营,微信deep3321