Factory IO演示:输入输出操作的依赖管理示例
在现代Swift应用开发中,依赖注入(Dependency Injection)已成为构建可测试、可维护代码的关键技术。Factory作为一个轻量级但功能强大的依赖注入框架,特别适合处理输入输出(IO)操作的依赖管理。本文将深入探讨如何使用Factory来管理IO操作的依赖关系,并通过实际示例展示其强大功能。
为什么IO操作需要依赖注入?
IO操作(如文件读写、网络请求、数据库访问等)具有以下特点:
- 副作用:会改变外部状态或受外部状态影响
- 不确定性:可能失败或返回不同结果
- 性能影响:可能阻塞线程或消耗资源
- 测试困难:难以在单元测试中模拟
通过依赖注入,我们可以:
- 解耦业务逻辑与具体IO实现
- 便于测试时替换真实IO操作为模拟实现
- 统一管理IO操作的配置和生命周期
Factory核心概念快速回顾
基本工厂定义
import FactoryKit
// 定义文件操作协议
protocol FileManagerProtocol {
func readFile(at path: String) throws -> Data
func writeFile(_ data: Data, to path: String) throws
}
// 实现真实文件操作
class RealFileManager: FileManagerProtocol {
func readFile(at path: String) throws -> Data {
try Data(contentsOf: URL(fileURLWithPath: path))
}
func writeFile(_ data: Data, to path: String) throws {
try data.write(to: URL(fileURLWithPath: path))
}
}
// 在容器中定义工厂
extension Container {
var fileManager: Factory<FileManagerProtocol> {
self { RealFileManager() }
}
}
依赖注入使用
class DataProcessor {
@Injected(\.fileManager) private var fileManager
func processFile(at path: String) throws -> String {
let data = try fileManager.readFile(at: path)
// 处理数据逻辑
return String(data: data, encoding: .utf8) ?? ""
}
}
IO操作依赖管理实战
1. 网络请求依赖管理
具体实现:
// 网络服务协议
protocol NetworkServiceProtocol {
func fetchData(from url: String) async throws -> Data
}
// 真实网络服务实现
class RealNetworkService: NetworkServiceProtocol {
func fetchData(from url: String) async throws -> Data {
guard let url = URL(string: url) else {
throw NetworkError.invalidURL
}
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
}
// 模拟网络服务用于测试
class MockNetworkService: NetworkServiceProtocol {
var mockData: Data?
var shouldThrowError = false
func fetchData(from url: String) async throws -> Data {
if shouldThrowError {
throw NetworkError.requestFailed
}
return mockData ?? Data()
}
}
// 容器配置
extension Container {
var networkService: Factory<NetworkServiceProtocol> {
self { RealNetworkService() }
}
}
// 数据获取器使用依赖
class DataFetcher {
@Injected(\.networkService) private var networkService
func fetchUserData() async throws -> User {
let data = try await networkService.fetchData(from: "https://api.example.com/user")
return try JSONDecoder().decode(User.self, from: data)
}
}
2. 数据库操作依赖管理
// 数据库操作协议
protocol DatabaseProtocol {
func save<T: Encodable>(_ object: T, forKey key: String) throws
func load<T: Decodable>(_ type: T.Type, forKey key: String) throws -> T?
func delete(forKey key: String) throws
}
// UserDefaults实现
class UserDefaultsDatabase: DatabaseProtocol {
private let userDefaults: UserDefaults
init(userDefaults: UserDefaults = .standard) {
self.userDefaults = userDefaults
}
func save<T: Encodable>(_ object: T, forKey key: String) throws {
let data = try JSONEncoder().encode(object)
userDefaults.set(data, forKey: key)
}
func load<T: Decodable>(_ type: T.Type, forKey key: String) throws -> T? {
guard let data = userDefaults.data(forKey: key) else { return nil }
return try JSONDecoder().decode(type, from: data)
}
func delete(forKey key: String) throws {
userDefaults.removeObject(forKey: key)
}
}
// 容器配置
extension Container {
var database: Factory<DatabaseProtocol> {
self { UserDefaultsDatabase() }
}
}
3. 文件IO操作的高级管理
带缓存的文件读取器:
protocol FileReaderProtocol {
func readFileWithCache(at path: String) async throws -> Data
}
class CachedFileReader: FileReaderProtocol {
@Injected(\.fileManager) private var fileManager
@Injected(\.cacheService) private var cacheService
func readFileWithCache(at path: String) async throws -> Data {
// 先检查缓存
if let cachedData = try await cacheService.getData(forKey: path) {
return cachedData
}
// 缓存未命中,读取文件
let data = try fileManager.readFile(at: path)
// 缓存结果
try await cacheService.setData(data, forKey: path)
return data
}
}
// 缓存服务协议
protocol CacheServiceProtocol {
func getData(forKey key: String) async throws -> Data?
func setData(_ data: Data, forKey key: String) async throws
}
// 容器配置
extension Container {
var fileReader: Factory<FileReaderProtocol> {
self { CachedFileReader() }
}
var cacheService: Factory<CacheServiceProtocol> {
self { MemoryCacheService() }
}
}
测试中的IO依赖管理
单元测试示例
import XCTest
import FactoryKit
class DataProcessorTests: XCTestCase {
override func setUp() {
super.setUp()
Container.shared.reset()
}
func testProcessFileSuccess() async throws {
// 配置模拟文件管理器
let mockData = "test content".data(using: .utf8)!
Container.shared.fileManager.register {
MockFileManager(mockData: mockData)
}
let processor = DataProcessor()
let result = try processor.processFile(at: "test.txt")
XCTAssertEqual(result, "test content")
}
func testProcessFileFailure() async throws {
// 配置抛出错误的文件管理器
Container.shared.fileManager.register {
MockFileManager(shouldThrowError: true)
}
let processor = DataProcessor()
XCTAssertThrowsError(try processor.processFile(at: "test.txt")) { error in
XCTAssertTrue(error is FileError)
}
}
}
// 模拟文件管理器
class MockFileManager: FileManagerProtocol {
let mockData: Data?
let shouldThrowError: Bool
init(mockData: Data? = nil, shouldThrowError: Bool = false) {
self.mockData = mockData
self.shouldThrowError = shouldThrowError
}
func readFile(at path: String) throws -> Data {
if shouldThrowError {
throw FileError.readFailed
}
guard let data = mockData else {
throw FileError.fileNotFound
}
return data
}
func writeFile(_ data: Data, to path: String) throws {
if shouldThrowError {
throw FileError.writeFailed
}
// 模拟写入成功
}
}
预览环境配置
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
// 为预览配置模拟IO服务
Container.shared.fileManager.preview {
MockFileManager(mockData: "Preview content".data(using: .utf8))
}
Container.shared.networkService.preview {
MockNetworkService(mockData: previewUserJSON.data(using: .utf8))
}
return ContentView()
}
}
高级IO依赖模式
1. 参数化工厂
// 支持配置参数的数据库工厂
extension Container {
var configurableDatabase: ParameterFactory<DatabaseConfig, DatabaseProtocol> {
self { config in
switch config.type {
case .userDefaults:
return UserDefaultsDatabase()
case .fileSystem:
return FileSystemDatabase(basePath: config.basePath)
case .inMemory:
return InMemoryDatabase()
}
}
}
}
// 使用示例
class ConfigManager {
func setupDatabase() {
let config = DatabaseConfig(type: .fileSystem, basePath: "/app/data")
let database = Container.shared.configurableDatabase(config)
// 使用配置好的数据库
}
}
2. 作用域管理
extension Container {
var sharedFileManager: Factory<FileManagerProtocol> {
self { RealFileManager() }
.singleton // 单例作用域
}
var cachedNetworkService: Factory<NetworkServiceProtocol> {
self { RealNetworkService() }
.scope(.cached) // 缓存作用域
}
var sessionScopedService: Factory<SessionServiceProtocol> {
self { SessionService() }
.scope(.session) // 会话作用域
}
}
3. 装饰器模式
// 日志装饰器
class LoggingFileManagerDecorator: FileManagerProtocol {
private let wrapped: FileManagerProtocol
private let logger: LoggerProtocol
init(wrapped: FileManagerProtocol, logger: LoggerProtocol) {
self.wrapped = wrapped
self.logger = logger
}
func readFile(at path: String) throws -> Data {
logger.log("Reading file at: \(path)")
let data = try wrapped.readFile(at: path)
logger.log("Successfully read \(data.count) bytes")
return data
}
func writeFile(_ data: Data, to path: String) throws {
logger.log("Writing \(data.count) bytes to: \(path)")
try wrapped.writeFile(data, to: path)
logger.log("Successfully wrote file")
}
}
// 容器配置带装饰器的文件管理器
extension Container {
var loggingFileManager: Factory<FileManagerProtocol> {
self {
LoggingFileManagerDecorator(
wrapped: RealFileManager(),
logger: ConsoleLogger()
)
}
}
}
性能优化建议
1. 懒加载与缓存策略
class OptimizedDataService {
@LazyInjected(\.fileManager) private var fileManager
@LazyInjected(\.cacheService) private var cacheService
private var cache: [String: Data] = [:]
func getData(forKey key: String) async throws -> Data {
if let cached = cache[key] {
return cached
}
let data = try fileManager.readFile(at: key)
cache[key] = data
return data
}
}
2. 并发安全访问
actor ConcurrentFileManager: FileManagerProtocol {
private let wrapped: FileManagerProtocol
private var accessQueue: [String: Task<Data, Error>] = [:]
init(wrapped: FileManagerProtocol) {
self.wrapped = wrapped
}
func readFile(at path: String) throws -> Data {
// 防止对同一文件的并发重复读取
if let existingTask = accessQueue[path] {
return try await existingTask.value
}
let task = Task {
defer { accessQueue[path] = nil }
return try wrapped.readFile(at: path)
}
accessQueue[path] = task
return try await task.value
}
func writeFile(_ data: Data, to path: String) throws {
try wrapped.writeFile(data, to: path)
}
}
总结
通过Factory框架管理IO操作依赖,我们可以获得以下优势:
| 优势 | 说明 |
|---|---|
| 可测试性 | 轻松替换真实IO操作为模拟实现 |
| 解耦性 | 业务逻辑与具体IO实现分离 |
| 可配置性 | 根据不同环境配置不同的IO策略 |
| 可维护性 | 统一的依赖管理接口 |
| 性能优化 | 支持缓存、懒加载等优化策略 |
Factory提供的强大依赖注入机制,特别适合处理IO操作这种具有副作用和不确定性的场景。通过合理的依赖管理,我们可以构建出更加健壮、可测试、可维护的应用程序。
在实际项目中,建议根据具体需求选择合适的依赖注入策略,充分利用Factory提供的各种特性,如作用域管理、参数化工厂、装饰器模式等,来构建高效的IO操作依赖管理体系。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



