8.2 KiB
8.2 KiB
SOLID 设计原则重构指南
🎯 重构目标
使用SOLID设计原则优化media_manager.py,提高代码的可维护性、可扩展性和可测试性。
📋 SOLID 原则应用
1. 单一职责原则 (SRP - Single Responsibility Principle)
重构前问题
MediaManager类承担了太多职责:依赖管理、视频信息提取、场景检测、文件管理等
重构后改进
# 依赖管理器 - 只负责管理依赖
class DependencyManager:
def __init__(self):
self._dependencies = {}
self._initialize_dependencies()
# 视频信息提取器 - 只负责提取视频信息
class FFProbeVideoInfoExtractor(VideoInfoExtractor):
def extract_video_info(self, file_path: str) -> Dict:
# 专门负责使用ffprobe提取视频信息
# 场景检测器 - 只负责场景检测
class PySceneDetectSceneDetector(SceneDetector):
def detect_scenes(self, file_path: str, threshold: float) -> List[float]:
# 专门负责使用PySceneDetect检测场景
# 工厂类 - 只负责创建对象
class VideoProcessorFactory:
def create_video_info_extractor(self) -> VideoInfoExtractor:
# 专门负责创建合适的提取器
2. 开闭原则 (OCP - Open/Closed Principle)
重构前问题
- 添加新的视频处理方法需要修改现有代码
重构后改进
# 抽象接口 - 对扩展开放,对修改关闭
class VideoInfoExtractor(ABC):
@abstractmethod
def extract_video_info(self, file_path: str) -> Dict:
pass
class SceneDetector(ABC):
@abstractmethod
def detect_scenes(self, file_path: str, threshold: float) -> List[float]:
pass
# 新的实现可以轻松添加,无需修改现有代码
class NewVideoInfoExtractor(VideoInfoExtractor):
def extract_video_info(self, file_path: str) -> Dict:
# 新的实现方法
pass
3. 里氏替换原则 (LSP - Liskov Substitution Principle)
重构后实现
# 任何VideoInfoExtractor的子类都可以替换基类
def process_video(extractor: VideoInfoExtractor, file_path: str):
info = extractor.extract_video_info(file_path) # 可以是任何实现
return info
# FFProbe实现
ffprobe_extractor = FFProbeVideoInfoExtractor()
info1 = process_video(ffprobe_extractor, "video.mp4")
# OpenCV实现
opencv_extractor = OpenCVVideoInfoExtractor(dependency_manager)
info2 = process_video(opencv_extractor, "video.mp4")
4. 接口隔离原则 (ISP - Interface Segregation Principle)
重构前问题
- 大而全的接口,客户端被迫依赖不需要的方法
重构后改进
# 专门的接口,客户端只依赖需要的功能
class VideoInfoExtractor(ABC):
@abstractmethod
def extract_video_info(self, file_path: str) -> Dict:
pass
class SceneDetector(ABC):
@abstractmethod
def detect_scenes(self, file_path: str, threshold: float) -> List[float]:
pass
class VideoProcessor(ABC):
@abstractmethod
def split_video(self, input_path: str, output_dir: str, scene_times: List[float]) -> List[str]:
pass
5. 依赖倒置原则 (DIP - Dependency Inversion Principle)
重构前问题
- 高层模块直接依赖低层模块的具体实现
重构后改进
class MediaManager:
def __init__(self,
dependency_manager: DependencyManager = None,
video_info_extractor: VideoInfoExtractor = None,
scene_detector: SceneDetector = None):
# 依赖注入 - 依赖抽象而不是具体实现
self.dependency_manager = dependency_manager or globals()['dependency_manager']
self.factory = VideoProcessorFactory(self.dependency_manager)
# 延迟初始化处理器
self._video_info_extractor = video_info_extractor
self._scene_detector = scene_detector
@property
def video_info_extractor(self) -> VideoInfoExtractor:
"""懒加载 - 依赖抽象接口"""
if self._video_info_extractor is None:
self._video_info_extractor = self.factory.create_video_info_extractor()
return self._video_info_extractor
🏗️ 重构架构
新的类层次结构
DependencyManager (依赖管理)
├── 检查和管理所有外部依赖
└── 提供统一的依赖访问接口
VideoInfoExtractor (抽象接口)
├── FFProbeVideoInfoExtractor (ffprobe实现)
└── OpenCVVideoInfoExtractor (OpenCV实现)
SceneDetector (抽象接口)
├── PySceneDetectSceneDetector (PySceneDetect实现)
└── OpenCVSceneDetector (OpenCV实现)
VideoProcessorFactory (工厂类)
├── 创建VideoInfoExtractor实例
└── 创建SceneDetector实例
MediaManager (协调器)
├── 使用依赖注入
├── 通过工厂创建处理器
└── 协调各个组件工作
🔧 使用示例
1. 基本使用 (使用默认实现)
# 自动选择最佳实现
media_manager = MediaManager()
video_info = media_manager._get_video_info("video.mp4")
scene_changes = media_manager._detect_scene_changes("video.mp4")
2. 依赖注入使用
# 手动指定实现
dependency_manager = DependencyManager()
video_extractor = FFProbeVideoInfoExtractor()
scene_detector = PySceneDetectSceneDetector(dependency_manager)
media_manager = MediaManager(
dependency_manager=dependency_manager,
video_info_extractor=video_extractor,
scene_detector=scene_detector
)
3. 测试友好
# 轻松进行单元测试
class MockVideoInfoExtractor(VideoInfoExtractor):
def extract_video_info(self, file_path: str) -> Dict:
return {"duration": 10.0, "width": 1920, "height": 1080}
# 注入Mock对象进行测试
mock_extractor = MockVideoInfoExtractor()
media_manager = MediaManager(video_info_extractor=mock_extractor)
📈 重构收益
1. 可维护性提升
- ✅ 单一职责: 每个类只负责一个功能,易于理解和修改
- ✅ 代码解耦: 组件之间松耦合,修改一个不影响其他
- ✅ 清晰架构: 层次分明,职责明确
2. 可扩展性提升
- ✅ 新实现: 轻松添加新的视频处理方法
- ✅ 插件化: 支持插件式扩展
- ✅ 配置化: 可以通过配置选择不同实现
3. 可测试性提升
- ✅ 依赖注入: 轻松注入Mock对象
- ✅ 接口隔离: 可以独立测试每个组件
- ✅ 单元测试: 每个类都可以独立测试
4. 代码质量提升
- ✅ 类型安全: 使用抽象基类和类型注解
- ✅ 错误处理: 统一的错误处理机制
- ✅ 日志记录: 详细的日志记录
🚀 迁移指南
1. 向后兼容
# 保持向后兼容的全局变量
VIDEO_LIBS_AVAILABLE = dependency_manager.is_available('opencv')
SCENEDETECT_AVAILABLE = dependency_manager.is_available('scenedetect')
# 现有代码无需修改
media_manager = MediaManager() # 仍然可以正常工作
2. 渐进式迁移
- 第一阶段: 使用新的MediaManager,保持现有接口
- 第二阶段: 逐步使用依赖注入
- 第三阶段: 完全迁移到新架构
3. 性能优化
- 懒加载: 处理器只在需要时创建
- 缓存: 依赖管理器缓存检查结果
- 工厂模式: 统一的对象创建逻辑
🎯 最佳实践
1. 依赖管理
# 统一的依赖检查
if dependency_manager.is_available('scenedetect'):
# 使用PySceneDetect
else:
# 回退到OpenCV
2. 错误处理
# 统一的错误处理模式
try:
return self.video_info_extractor.extract_video_info(file_path)
except Exception as e:
logger.error(f"Failed to get video info: {e}")
return default_video_info()
3. 扩展新功能
# 添加新的视频处理器
class NewVideoProcessor(VideoProcessor):
def split_video(self, input_path: str, output_dir: str, scene_times: List[float]) -> List[str]:
# 新的实现
pass
# 在工厂中注册
class VideoProcessorFactory:
def create_video_processor(self) -> VideoProcessor:
if self.dependency_manager.is_available('new_library'):
return NewVideoProcessor()
else:
return DefaultVideoProcessor()
通过SOLID原则重构,代码变得更加模块化、可测试和可维护!