如何分离关注点:拆分PostManager的API调用与Post更新逻辑
问题描述
我有一个PostManager类作为单一数据源存储Post列表,代码如下:
class PostManager: ObservableObject { @Published var posts = [Post]() func updatePostMessage(postId: Int, message: String) { // 调用API并接收新的Post对象 if let newPost = data.data?.post { self.updateMessage(for: newPost) } } func updateMessage(for newPost: Post) { if let postIndex = posts.firstIndex(where: {$0.id == newPost.id}) { posts[postIndex].message = newPost.message } } }
Post结构体定义:
struct Post: Codable, Identifiable { var id: Int var title: String var message: String var createdAt: String? var updatedAt: String? // 其他更多属性... enum CodingKeys: String, CodingKey { case id = "id" case title = "title" case message = "message" case createdAt = "created_at" case updatedAt = "updated_at" } }
现在需要在PostManager中新增数十个API调用函数,以及数十个类似updateMessage的Post属性更新函数。要求将API调用函数保留在PostManager中,把Post更新函数迁移到另一个类,同时保证@Published var posts能正常触发更新。
解决方案
1. 新建Post更新类
创建PostUpdater类,专门处理所有Post属性的更新逻辑,通过弱引用持有PostManager,避免循环引用的同时可以直接操作其posts数组:
class PostUpdater { private weak var postManager: PostManager? init(postManager: PostManager) { self.postManager = postManager } // 迁移原有的message更新逻辑 func updateMessage(for newPost: Post) { guard let posts = postManager?.posts else { return } if let postIndex = posts.firstIndex(where: {$0.id == newPost.id}) { postManager?.posts[postIndex].message = newPost.message } } // 新增title更新方法 func updateTitle(for newPost: Post) { guard let posts = postManager?.posts else { return } if let postIndex = posts.firstIndex(where: {$0.id == newPost.id}) { postManager?.posts[postIndex].title = newPost.title } } // 其他属性更新方法以此类推... }
2. 改造PostManager集成更新类
在PostManager中初始化PostUpdater,API调用函数直接调用更新类的方法即可:
class PostManager: ObservableObject { @Published var posts = [Post]() private let postUpdater: PostUpdater init() { self.postUpdater = PostUpdater(postManager: self) } func updatePostMessage(postId: Int, message: String) { // 调用API获取新Post对象 if let newPost = data.data?.post { postUpdater.updateMessage(for: newPost) } } // 新增的title更新API调用函数 func updatePostTitle(postId: Int, title: String) { // 调用API获取新Post对象 if let newPost = data.data?.post { postUpdater.updateTitle(for: newPost) } } // 更多API调用方法... }
3. 优化:通用更新方法减少重复代码
如果每个更新逻辑都是替换单个属性,或者同步整个Post对象,可以写通用方法避免重复代码:
class PostUpdater { private weak var postManager: PostManager? init(postManager: PostManager) { self.postManager = postManager } // 通用属性更新方法,通过KeyPath指定要更新的属性 func updatePost<Value>(withId postId: Int, keyPath: WritableKeyPath<Post, Value>, value: Value) { guard let posts = postManager?.posts else { return } if let postIndex = posts.firstIndex(where: {$0.id == postId}) { postManager?.posts[postIndex][keyPath: keyPath] = value } } // 同步整个Post对象的通用方法 func syncPost(with newPost: Post) { guard let posts = postManager?.posts else { return } if let postIndex = posts.firstIndex(where: {$0.id == newPost.id}) { postManager?.posts[postIndex] = newPost } } }
对应的PostManager调用可以简化:
func updatePostMessage(postId: Int, message: String) { if let newPost = data.data?.post { // 方式1:更新单个属性 postUpdater.updatePost(withId: newPost.id, keyPath: \.message, value: newPost.message) // 方式2:直接同步整个Post对象 postUpdater.syncPost(with: newPost) } }
原理说明
PostManager的posts被@Published修饰,只要直接修改数组中的元素(或替换数组元素),就会触发ObservableObject的变更通知,保证UI正常更新。PostUpdater通过弱引用持有PostManager,既可以操作数据源,又不会引发循环引用问题。
内容的提问来源于stack exchange,提问作者j1834982
相关产品推荐
相关产品推荐

