281 lines
8.2 KiB
Markdown
281 lines
8.2 KiB
Markdown
# 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原则重构,代码变得更加模块化、可测试和可维护!*
|