用Typhoon注射相同的属性两次

时间:2015-02-25 09:58:37

标签: swift dependency-injection typhoon

我计划在新创建的项目中引入依赖注入框架,并且发现Typhoon看起来非常好(除了在Swift中引用方法和属性时必须使用字符串)。它通常看起来很适合我的应用程序,但我遇到了处理网络请求的构造问题。

我的所有View控制器(需要网络访问)都继承自ServiceViewController,我用它来懒惰地初始化服务(此上下文中的服务是用于处理网络通信的类):

class ServiceViewController {    
  var services = [BDService]()//a list of (lazily initialized) services used by the the view controller

  //create a service of the specified type (or return from the existing list if it has already been created)
  func getService<T : BDService>() -> T {
    for service in services {
        if service is T {
            return service as T
        }
    }
    let klass : T.Type = T.self
    let newService : T = klass()//don't ask - due to Swift bug: http://stackoverflow.com/questions/27336607
    services.append(newService)
    return newService
  }

  //tell all initialized services to cancel pending requests
  func cancelAllPendingTasks() {
    services.each { $0.cancelPendingTasks() }
  }

  //inform whether a request has been sent and we are still waiting for a response
  func hasPendingTasks() -> Bool {
    return services.any { $0.pendingTasks.count > 0 }
  }
}

....

//usage from MyViewController:
let accountService : BDAccountService = super.getService()
/* do something on accountService */
...
let postingService : BDPostingService = super.getService()
/* do something on postingService */
...
super.cancelAllPendingTasks() //e.g. if user clicks back button

最后一种方法cancelPendingTaskshasPendingTasks是我想要保留这种架构的原因。通过在列表中提供ViewController的所有服务,我们可以确保我们不会忘记取消其中一个服务上的待处理任务。

现在,当我想介绍Typhoon时,我可以在初始化程序中注入我的所有服务:

public dynamic func myViewController() -> AnyObject {
    return TyphoonDefinition.withClass(MyViewController.self) {
        (definition) in
        definition.useInitializer("initWithAccountService:postingService:") {//(forgive me if I wrote this wrong)
            (initializer) in
            initializer.injectParameterWith(self.accountService())
            initializer.injectParameterWith(self.postingService())
        }
    }
}

这种方法有两个缺点:

  1. 取消所有任务(或类似任务)需要调用所有服务
  2. 无论何时需要,服务都不会被懒惰地初始化
  3. 广告1)现在处理cancelAllPendingTasks()的一种方法是将两个服务添加到初始化程序的ServiceViewController.services列表中。然后cancelAllPendingTasks()将能够访问所有服务。但我正在寻找一种避免不必这样做的方法,以便以后添加服务不需要“记住这样做那个”

    Ad 2)我没有看到使用依赖注入的方法。但我不认为这是一个大问题,因为服务的内存占用可能不那么值得注意: - )

    所以我的问题是:我能否以某种方式同时将服务注入构造函数到服务列表中?

    修改 当我从@JasperBlues的回答中发现很容易将两个相同的对象注入属性和初始化器时,我的第二个问题对我来说更受关注:如果我们将一堆服务注入初始化器,但忘记注入完全相同的服务进入数组(例如我们忘记了其中一个),我们得到了很难找到的错误,因为在hasPendingTasks方法有一天返回错误的结果之前没有人会发现它,并且由于这个原因而挂起了一些东西。这是我原始设计的一个不可能的场景,但如果我们应该两次注入服务,我们突然发生重复错误,容易出错。

2 个答案:

答案 0 :(得分:1)

Typhoon允许您选择使用初始化程序注入(或不使用)以及以下任何一项:

  • 物业注入
  • 方法注入(带有一个或多个参数)。
  • 其他配置,例如指定在注入后调用的回调方法或设置范围。

如果已经通过初始化程序设置了该属性,则使用属性注入没有问题。

对于相同的方法,可以多次调用方法注入。 (例如,收集日志目录名称的方法)。

citiesListController中的Typhoon Sample Application显示了将初始化程序注入与属性注入混合的示例。 user guide显示了如何进行方法注入,设置范围或在注入发生后调用回调方法。

答案 1 :(得分:0)

在更多地解决我的问题并实施Typhoon之后,我有一些想法要分享:

  1. 两次注入对象非常容易:

    public dynamic func myViewController() -> AnyObject {
        return TyphoonDefinition.withClass(MyViewController.self) {
            $0.useInitializer("initWithAccountService:postingService:") {
                $0.injectParameterWith(self.accountService())
                $0.injectParameterWith(self.postingService())
            }
            $0.injectProperty("services", with:[self.accountService(), self.postingService()])
        }
    }
    

    这会两次注入相同的对象,并且不会创建两个accountService和postingService实例。在阅读@JasperBlues的回答之前,我不清楚这一点。

  2. 我对重复注入accountService和postService的担忧实际上很容易克服。我刚刚删除了初始化程序并返回使用getService<T>()方法。因此,我的装配现在看起来像这样:

    public dynamic func myViewController() -> AnyObject {
         return TyphoonDefinition.withClass(MyViewController.self) {
             $0.injectProperty("services", with:[self.accountService(), self.postingService()])
         }
    }
    

    上面填充了services数组,因此getService<T>()方法将从此数组中返回正确的注入对象。

  3. 关于延迟初始化的第三个问题实际上仍然可以通过简单地不注入服务来克服,但是如前所述直接在代码中创建它们。毕竟,由于可能注入这些服务,因此测试它们仍然很容易。尽管如此,由于其他问题(如在一个地方指定了依赖关系,即大会),注入它们可能会更好: - )

  4. 但毕竟,这是一次很好的学习经历,现在Typhoon正在进入我的项目: - )