mxivideo/docs/solid-design-refactoring.md

281 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SOLID 设计原则重构指南
## 🎯 重构目标
使用SOLID设计原则优化media_manager.py提高代码的可维护性、可扩展性和可测试性。
## 📋 SOLID 原则应用
### 1. 单一职责原则 (SRP - Single Responsibility Principle)
#### 重构前问题
- `MediaManager`类承担了太多职责:依赖管理、视频信息提取、场景检测、文件管理等
#### 重构后改进
```python
# 依赖管理器 - 只负责管理依赖
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)
#### 重构前问题
- 添加新的视频处理方法需要修改现有代码
#### 重构后改进
```python
# 抽象接口 - 对扩展开放,对修改关闭
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)
#### 重构后实现
```python
# 任何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)
#### 重构前问题
- 大而全的接口,客户端被迫依赖不需要的方法
#### 重构后改进
```python
# 专门的接口,客户端只依赖需要的功能
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)
#### 重构前问题
- 高层模块直接依赖低层模块的具体实现
#### 重构后改进
```python
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. 基本使用 (使用默认实现)
```python
# 自动选择最佳实现
media_manager = MediaManager()
video_info = media_manager._get_video_info("video.mp4")
scene_changes = media_manager._detect_scene_changes("video.mp4")
```
### 2. 依赖注入使用
```python
# 手动指定实现
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. 测试友好
```python
# 轻松进行单元测试
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. 向后兼容
```python
# 保持向后兼容的全局变量
VIDEO_LIBS_AVAILABLE = dependency_manager.is_available('opencv')
SCENEDETECT_AVAILABLE = dependency_manager.is_available('scenedetect')
# 现有代码无需修改
media_manager = MediaManager() # 仍然可以正常工作
```
### 2. 渐进式迁移
1. **第一阶段**: 使用新的MediaManager保持现有接口
2. **第二阶段**: 逐步使用依赖注入
3. **第三阶段**: 完全迁移到新架构
### 3. 性能优化
- **懒加载**: 处理器只在需要时创建
- **缓存**: 依赖管理器缓存检查结果
- **工厂模式**: 统一的对象创建逻辑
## 🎯 最佳实践
### 1. 依赖管理
```python
# 统一的依赖检查
if dependency_manager.is_available('scenedetect'):
# 使用PySceneDetect
else:
# 回退到OpenCV
```
### 2. 错误处理
```python
# 统一的错误处理模式
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. 扩展新功能
```python
# 添加新的视频处理器
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原则重构代码变得更加模块化、可测试和可维护*