精选文章

25. 架构实战

2019-11-07 · 架构

25. 架构实战

架构的目标是“可维护、可扩展、可测试”。核心做法:拆分职责,业务与 UI 解耦,依赖可替换。

一、MVC 的问题点

  • Controller 过胖
  • 网络、业务、UI 混在一起
  • 单元测试难做

二、MVVM 最小实现

1. Model

struct User: Decodable {
    let id: Int
    let name: String
}

2. ViewModel

final class UserViewModel {
    private let service: UserService

    init(service: UserService) {
        self.service = service
    }

    var onUpdate: ((User) -> Void)?
    var onError: ((Error) -> Void)?

    func loadUser() {
        service.fetch { [weak self] result in
            switch result {
            case .success(let user): self?.onUpdate?(user)
            case .failure(let error): self?.onError?(error)
            }
        }
    }
}

3. ViewController

final class UserVC: UIViewController {
    private let nameLabel = UILabel()
    private let viewModel: UserViewModel

    init(viewModel: UserViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

    override func viewDidLoad() {
        super.viewDidLoad()
        viewModel.onUpdate = { [weak self] user in
            DispatchQueue.main.async { self?.nameLabel.text = user.name }
        }
        viewModel.onError = { error in
            print(error)
        }
        viewModel.loadUser()
    }
}

三、服务层与协议解耦

protocol UserService {
    func fetch(completion: @escaping (Result<User, Error>) -> Void)
}

final class RemoteUserService: UserService {
    func fetch(completion: @escaping (Result<User, Error>) -> Void) {
        // 网络请求
    }
}

通过协议,后续可以替换成 MockService 进行测试。

四、Repository 分层

protocol UserRepository {
    func fetch(completion: @escaping (Result<User, Error>) -> Void)
}

final class UserRepositoryImpl: UserRepository {
    private let remote: UserService
    private let cache: UserCache

    init(remote: UserService, cache: UserCache) {
        self.remote = remote
        self.cache = cache
    }

    func fetch(completion: @escaping (Result<User, Error>) -> Void) {
        if let cached = cache.load() {
            completion(.success(cached))
            return
        }
        remote.fetch { result in
            if case .success(let user) = result { self.cache.save(user) }
            completion(result)
        }
    }
}

五、模块化边界

  • Feature:具体业务模块
  • Core:网络、存储、工具
  • UI:通用组件

结构示例:

App
├─ Core
├─ Network
├─ Storage
├─ Features
│  ├─ Home
│  ├─ Profile
└─ UIComponents

六、依赖注入

final class AppContainer {
    let userService: UserService
    let userRepository: UserRepository

    init() {
        userService = RemoteUserService()
        userRepository = UserRepositoryImpl(remote: userService, cache: MemoryUserCache())
    }

    func makeUserVC() -> UIViewController {
        let vm = UserViewModel(service: userService)
        return UserVC(viewModel: vm)
    }
}

架构不是越复杂越好,目标是让代码在长期迭代中保持可读与可控。

JJ

作者简介

专注于内容创作、产品策略与设计实践。欢迎交流合作。

上一篇 下一篇