【问题标题】:Building architecture from TDD written "modules"从 TDD 编写的“模块”构建架构
【发布时间】:2017-02-19 18:54:21
【问题描述】:

我对 TDD 很陌生,我正在使用 TDD 编写我当前的项目。我一直在尝试尽可能多地使用 TDD,到目前为止它正在奏效。

我项目中的每个“模块”都是独立的。它们都可以在不依赖项目其他部分的依赖项的情况下进行测试,我可以注入“模拟”依赖项。

其中之一是CoreDatamanagedObjectContext,用于保存和查找项目中的项目。

所以现在我正在尝试将所有内容放在一起,并想知道如何最好地构建它们。

例如。我可能在应用程序的深处有一个视图控制器,它有一个服务可以将一些东西保存到Core Data,所以这个服务“模块”需要managedObjectContext 来做到这一点。

我应该怎么去那里?

我真的需要通过一系列实际上不需要它的对象来传递托管对象上下文吗?这似乎破坏了我正在努力的一切?

我可以使用单例,但想避免它,因为它只会引起疼痛(根据以前的经验)。

我应该怎么做?

【问题讨论】:

  • 从 Java 的角度来看;我只能说:对于 Java,你会寻找其他依赖注入的框架。不需要进行测试的注入;但在实际运行时——一个理解例如知道如何实例化这样一个 CoreData 对象的系统;然后使其可用于需要此类对象的“客户端代码”。然后:您在创建所有模块之后开始考虑这些问题,这听起来有点奇怪。当然,TDD 更多的是自下而上;但是您不应该在完成所有模块之前完成某种程度的“自上而下”设计吗?!
  • @GhostCat 好的,所以不必传递核心数据的托管对象上下文,我应该让“模块”知道如何实例化它。问题在于应该在整个应用程序中使用托管对象上下文。只有其中一个......这让我重新考虑我应该只使用单例作为托管对象上下文,并且在产品中只获取单例实例。嗯……
  • @GhostCat RE 最后一个问题。我还没有完成所有模块。在深入了解需要它的模块的 TDD 之前,我现在正在考虑自上而下的设计。我不想走得太远,然后意识到我需要一种完全不同的方法。 :)
  • 当然,singleton 在这里可能是一个务实的答案。但是框架的重点是它也应该知道这些事情。可以告诉一个好的 DI 框架:这是该类的 一个 对象;所以要确保每个需要这样一个对象的人都收到 那个一个。
  • A) 我们正在进入讨论模式(这是我对这样一个几乎太宽泛的问题所期望的)和 B) 不要过于关注细节。我的主要观点是:也许如果有适合你的目标平台的框架,你想看看;如果是这样;您开始查看他们为您提供的服务 - 以决定是否适合进入该行业;或者如果您需要其他解决方案/技术/想法。

标签: ios core-data architecture


【解决方案1】:

我们也在使用 TDD 方法编写我们的项目。以下是一些细节:

  • 我们正在使用VIPER architecture.
  • 每个模块都是一个 VIPER 模块。
  • 依赖注入 (Swinject) 用于 viper 模块的初始化和添加依赖项。
  • 用于按模块处理应用程序事件的守护进程。
  • 公共服务层负责与服务器和本地存储通信。

我们内部开发了Cobra framework(使用swinject)用于引导和路由, Medusa 用于守护进程。我们已将这些项目开源,但仍需要一些文档。

这是我们应用程序的架构图。

回答您的用例:

ViewController 不应该有服务实例。您应该使用交互器进行交互 与服务层。 如果您使用cobramedusa,那么您可以像这样定义您的程序集:

// store assembly

container.register(StoreManager.self) { resolver in
   return StoreManager(...) 
}.inObjectScope(.Container) // for singleton


// service assembly

container.register(LoginServiceType.self) { resolver in
   return LoginService(store: resolver.resolve(StoreManager.self)!)
}

container.register(UserServiceType.self) { resolver in
   return USerService(store: resolver.resolve(StoreManager.self)!)
}


// Login Service

class LoginService {
   let store: StoreManager
   init(store: StoreManager) {
      self.store = store
  }

  func login() {
    store....()
  }
}


// User service

class UserService {
   let store: StoreManager
   init(store: StoreManager) {
      self.store = store
   }

   func fetchUserDetails(id: String) {
      store....()
   }
}

了解 DI 的强大功能,现在您无需担心“存储(任何其他对象)实例”的创建和传递。基本上,您需要设置您的程序集,一切就绪。

【讨论】:

  • 优秀。非常感谢。我将进一步研究他的模式。它实际上看起来有点像我已经开始开发的结构,作为 TDD 的必需品。没有 DI,几乎不可能测试单个单元。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-25
相关资源
最近更新 更多