1.在开发log功能的时候,发现点击发送数据后,没有反应(初始化陷阱)
经检查(很漫长的检查)后发现在最后面加了上次遗留的初始化导致的,相当于初始化两次,
因为加了log功能后需要传入两个实例的构造函数,但是在最后面又重新进入初始化了,但是没有传入参数,导致_logger为null。
后续debug引入一个日志验证
发现一初始化就为null,后续要注意这个
- 先初始化基础服务(如日志器)
- 再初始化依赖它们的服务
2.空对象模式应用
在使用log记录的时候,设计模式特别适合像空日志器这样的"无状态工具对象",它提供了接口实现的灵活性,同时保持了资源使用的最优化
在 NullSerialPortLogger中使用私有构造函数是为了:
- 强制单例模式:确保全局只有一个实例
- 防止误用:避免开发者创建不必要的实例
- 优化资源:减少内存占用和初始化开销
- 保持一致性:所有使用者共享相同的行为
单例模式在日志系统中的优势:
1.内存效率
// 使用单例:整个应用只有一个实例
var logger1 = NullSerialPortLogger.Instance;
var logger2 = NullSerialPortLogger.Instance;
// logger1 == logger2,内存中只有一个对象
2.简化访问
// 任何地方都可以直接访问
public class MyService
{
private ISerialPortLogger _logger = NullSerialPortLogger.Instance;
// 不需要构造函数注入
}
3.空对象模式
// 作为空对象模式的实现
public class SerialPortService
{public SerialPortService(ISerialPortLogger logger = null)
{// 如果没有提供日志器,使用空日志器单例
_logger = logger ?? NullSerialPortLogger.Instance;
}
}
3.构造函数设计原则
- 所有必需依赖都应作为参数
- 使用参数验证确保依赖不为null
- 避免在构造函数中执行复杂逻辑
实现依赖注入
实现定时发送
ISerialPortService 的实现类 SerialPortService 包含定时发送、异常处理等逻辑。通过接口抽象,FrmHelper 可以:
• 调用 SendData 和 StartTimedSend 方法时,无需关心具体实现细节。
• 在需要时替换为其他实现(如带日志或性能监控的版本)。
总结
这种写法的核心目的是 解耦 和 提高扩展性,符合以下设计原则:
- 依赖倒置原则(DIP):依赖抽象(接口),不依赖具体类。
- 开闭原则(OCP):通过扩展而非修改实现功能变化。
- 单一职责原则(SRP):FrmHelper 专注于界面逻辑,不处理串口细节。
github地址:luis1900/SerialPortAssistant-OOP
gittee地址:https://gitee.com/beethoven1900/serial-port-assistant
浙公网安备 33010602011771号