不同架构的详细分析VIP架构

不同架构的详细分析

传统的MVC架构

**传统的MVC架构将系统分成三个部分:模型(Model)、视图(View)和控制器(Controller)。**模型表示数据和数据处理逻辑;视图表示用户界面;控制器接收用户输入并处理真正的动作。该架构的优点是简单易懂,容易开发和维护;缺点是当应用规模变大时,会出现控制器臃肿、业务逻辑混杂的问题,不利于代码重用和维护。

MVVM架构

**MVVM架构将系统分成三个部分:视图(View)、视图模型(ViewModel)和模型(Model)。**视图负责渲染和用户输入;视图模型处理逻辑并更新视图;模型则与数据相关。通过数据绑定机制,视图模型可以直接将改变反映到视图中,从而实现了低耦合、高可复用性的特点。缺点是实现复杂,需要实现双向数据绑定,代码量相对较多。

VIPER架构

** VIPER架构将系统分为五个部分:视图(View)、表示器(Presenter)、交互器(Interactor)、实体(Entity)和路由(Router)。**表示器负责将用户的操作解析成交互器可执行的操作;交互器负责执行具体的业务逻辑并返回结果给表示器;实体存储实际的业务逻辑,路由负责模块之间的路由和导航。该架构实现了单一职责原则(SRP),代码复用性高,且易于维护。缺点是分层过于细致,代码量稍多,有一定的上手难度。

Clean Swift(也称为 VIP)架构

** Clean Swift将系统分为六个部分:视图控制器(ViewController)、表示器(Presenter)、业务逻辑处理器(Interactor)、实体(Entity)、请求(Request)和响应(Response)。**Clean Swift 原则上是 VIPER 的变种,该架构推崇单一责任原则(SRP),实现了职责分离的目标,同时保证代码的可测试性和可维护性。在 Clean Swift 中,视图控制器只负责管理界面的生命周期和 UI 事件的处理,而将实际业务逻辑和数据处理都放在表示器、业务逻辑处理器和实体等组件中。该架构优点是职责分层明确,代码简洁可读,易于阅读和维护;同时提供了统一的请求、响应和错误处理接口,有利于提高代码复用性和可测试性。缺点是需要遵循约定式的编程规范。

demo

以下是使用 Clean Swift 架构编写的一个简单的 To-do List 应用程序的 demo,其包括了添加、查看、编辑和删除待办事项等功能。

1. 创建 Xcode 工程

  1. 打开 Xcode。
  2. 创建一个新的 iOS App 项目,并命名为ToDoListDemo
  3. 在项目设置中选择 Swift 语言和 Storyboard 用户界面。

2. 创建架构文件和类

  1. 在 Xcode 中,创建几个 Group,用于存放 Clean Swift 架构中的各个部分,例如:ModelsNetworkingScene 等。

  2. Scene Group 中,创建一个新的 Swift 文件,命名为 ToDoListModels.swift。在该文件中定义应用程序中使用的所有必要类型、结构体和模型。

    // 创建 TodoItem 结构体
    struct TodoItem {
        var id: Int
        var title: String
        var description: String?
        var isCompleted: Bool
        var date: Date?
    }
    
    // 创建 TodoListViewModel 结构体
    struct TodoListViewModel {
        var items: [TodoItem]
    }
    
  3. Scene Group 中,创建一个新的 Swift 文件,命名为 ToDoListWorker.swift。该文件将包含执行网络请求和数据持久化的代码。

    protocol TodoListWorkerProtocol {
        func fetchItems(completion: ([TodoItem]) -> Void)
        func saveItem(_ item: TodoItem, completion: @escaping (Bool) -> Void)
        func deleteItem(_ item: TodoItem, completion: @escaping (Bool) -> Void)
    }
    
    class TodoListWorker: TodoListWorkerProtocol {
    
        func fetchItems(completion: ([TodoItem]) -> Void) {
            // 在此处执行网络请求或者数据持久化操作
            // 返回获取到的 TodoItem 数据
        }
    
        func saveItem(_ item: TodoItem, completion: @escaping (Bool) -> Void) {
            // 在此处执行网络请求或者数据持久化操作
            completion(true)
        }
    
        func deleteItem(_ item: TodoItem, completion: @escaping (Bool) -> Void) {
            // 在此处执行网络请求或者数据持久化操作
            completion(true)
        }
    }
    
  4. Scene Group 中,创建一个新的 Swift 文件,命名为 ToDoListInteractor.swift。在该文件中创建操作 TodoItemTodoListInteractor 接口,并提供 TodoListInteractor 的默认实现。

    protocol TodoListInteractorProtocol {
        func fetchTodoList()
        func addTodoItem(item: TodoItem)
        // 添加更新和删除操作方法
    }
    
    class TodoListInteractor: TodoListInteractorProtocol {
        
        var presenter: TodoListPresenterProtocol?
        var worker: TodoListWorkerProtocol?
        
        func fetchTodoList() {
            worker?.fetchItems(completion: { [weak self] items in
                self?.presenter?.presentFetchedTodoList(response: items)
            })
        }
    
        func addTodoItem(item: TodoItem) {
            worker?.saveItem(item, completion: { [weak self] success in
                if success {
                    self?.fetchTodoList()
                }
            })
        }
        
        // 添加更新和删除操作方法的默认实现
    }
    
  5. Scene Group 中,创建一个新的 Swift 文件,命名为 ToDoListPresenter.swift。在该文件中创建表示获取到的 TodoItem 和操作后的 TodoListViewModelTodoListPresenter 接口,并提供 TodoListPresenter 的默认实现。

    protocol TodoListPresenterProtocol {
        func presentFetchedTodoList(response: [TodoItem])
        // 添加更新和删除操作方法
    }
    
    class TodoListPresenter: TodoListPresenterProtocol {
        
        weak var viewController: TodoListViewControllerProtocol?
    
        func presentFetchedTodoList(response: [TodoItem]) {
            let viewModel = TodoListViewModel(items: response)
            viewController?.displayFetchedTodoList(viewModel: viewModel)
        }
    
        // 添加更新和删除操作方法的默认实现
    }
    
  6. Scene Group 中,创建一个新的 Swift 文件,命名为 ToDoListViewController.swift。在该文件中创建表示用户界面的 TodoListViewController 和表示用户输入的 TodoListViewControllerInput 接口,并提供 TodoListViewController 的默认实现。

    protocol TodoListViewControllerProtocol: AnyObject {
        func displayFetchedTodoList(viewModel: TodoListViewModel)
    }
    
    protocol TodoListViewControllerInput {
        func displayAddTodoItemSuccess()
        // 添加更新和删除操作方法
    }
    
    class TodoListViewController: UIViewController, TodoListViewControllerProtocol {
        
        var interactor: TodoListInteractorProtocol?
        var router: TodoListRouterProtocol?
    
        // 实现 viewDidLoad() 和其他方法
    
        func displayFetchedTodoList(viewModel: TodoListViewModel) {
            // 更新用户界面以显示获取到的数据
        }
        
        // 添加更新和删除操作的默认实现
    }
    
  7. Scene Group 中,创建一个新的 Swift 文件,命名为 ToDoListRouter.swift。在该文件中创建表示导航逻辑的 TodoListRouter 接口,并提供 TodoListRouter 的默认实现。

    protocol TodoListRouterProtocol {
        func navigateToAddTodoItem()
        // 添加其他导航方法
    }
    
    class TodoListRouter: TodoListRouterProtocol {
        
        weak var viewController: UIViewController?
        
        // 实现 TodoListRouterProtocol 中的方法
        // 可以使用 NavigationController 或 Modal 方式实现不同的导航
    }
    

3. 生成视图控制器并完成基本逻辑

  1. 在 Xcode 中创建一个新的 UIViewController 类,命名为 ToDoListTableViewController,并在故事板中设置它为启动界面。

  2. 使用 Interface Builder 创建显示 TodoItem 的列表,并进行相应的布局和设计。

    注意:在该部分中只包含了部分代码。完整代码可以在我的 GitHub 仓库中找到:https://github.com/techsleep/clean-swift-todo-app-demo

  3. ToDoListTableViewController.swift 文件中,声明并初始化 interactorpresenterrouter

    class ToDoListTableViewController: UITableViewController {
        
        var interactor: TodoListInteractorProtocol?
        var presenter: TodoListPresenterProtocol?
        var router: TodoListRouterProtocol?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            interactor = TodoListInteractor()
            interactor?.presenter = presenter
            presenter = TodoListPresenter()
            presenter?.viewController = self
            router = TodoListRouter()
            router?.viewController = self
            interactor?.fetchTodoList()
            // 设置 NavigationController 样式和导航按钮等
        }
    }
    
  4. ToDoListTableViewController.swift 文件中,遵循 TodoListViewControllerInput 协议,并添加实现所需的操作方法。

    extension ToDoListTableViewController: TodoListViewControllerInput {
    
        func displayAddTodoItemSuccess() {
            // 在该回调中处理添加待办事项成功后的操作
        }
    
        // 添加更新和删除待办事项操作方法
    }
    
  5. ToDoListTableViewController.swift 文件中,实现用户输入操作时调用的方法,例如,添加待办事项或者导航到添加页面等。

    class ToDoListTableViewController: UITableViewController {
    
        @IBAction func addButtonClicked(_ sender: Any) {
            router?.navigateToAddTodoItem()
        }
        
        // 添加其他用户输入操作方法
    }
    
  6. TodoListRouter.swift 文件中,实现导航页面的具体逻辑。

    class TodoListRouter: TodoListRouterProtocol {
        
        weak var viewController: UIViewController?
        
        func navigateToAddTodoItem() {
            let addTodoItemVC = storyboard.instantiateViewController(withIdentifier: "AddTodoItemViewControllerID") as! AddTodoItemViewController
            addTodoItemVC.delegate = viewController as? AddTodoItemViewControllerDelegate
            viewController?.navigationController?.pushViewController(addTodoItemVC, animated: true)
        }
        
        // 添加其他导航
    }
    

4. 完成其余的逻辑

  1. AddTodoItemViewController 中,定义 AddTodoItemViewControllerDelegateTodoItem 对象,并通过协议通知 TodoListViewController 新的待办事项已经被添加。

  2. AddTodoItemViewController 中,使用 TodoListWorker 对象保存新的待办事项,然后通过协议通知 TodoListInteractor 数据已经更新。

  3. TodoListInteractor 中,使用 TodoListPresenter 对象更新用户界面以显示新的待办事项。

在完成以上操作后,您可以使用 Clean Swift 架构编写的 To-do List 应用程序的 demo 就可以正常运行了!