草庐IT

ios - 快速依赖注入(inject),具有两个 UIViewController 的依赖关系图,没有共同的父级

coder 2023-07-17 原文

当我们有两个在层次结构中非常深的 UIViewControllers 并且它们都需要保存状态的相同依赖项时,我们如何在不使用框架的情况下应用依赖注入(inject),而这两个 UIViewControllers 没有共同的父级。

例子:

VC1 -> VC2 -> VC3 -> VC4

VC5 -> VC6 -> VC7 -> VC8

让我们坐下来,他们都需要 VC4 和 VC8 UserService持有当前用户。

请注意,我们要避免单例。

有没有一种优雅的方法来处理这种 DI 情况?

经过一些研究,我发现有人提到了 Abstract Factory , Context interfaces , Builder , strategy pattern
但我找不到如何在 iOS 上应用它的例子

最佳答案

好的,我试试这个。

你说“没有单例”,所以我在下面排除了这一点,但也请看这个答案的底部。

Josh Homann 的评论已经是一个很好的解决方案的指针,但就我个人而言,我对协调器模式有疑问。

正如 Josh 正确说的那样, View Controller 不应该(很多)了解彼此 [1],但是如何例如传递/访问协调器或任何依赖项?有几种模式可以说明如何操作,但大多数模式都有一个基本上与您的要求背道而驰的问题:它们或多或少使协调器成为单例(本身或作为另一个单例的属性,如 AppDelegate )。协调器通常也被定义为单例(但并非总是如此,也不一定如此)。

我倾向于做的是依赖简单的初始化属性或 (最常见)懒惰的属性 和面向协议(protocol)的编程。让我们构建一个例子:UserService应是定义您的服务所需的所有功能的协议(protocol),MyUserService它的实现结构。让我们假设 UserService是一种设计结构,基本上用作某些用户相关数据的 getter/setter 系统:访问 token (例如保存在钥匙串(keychain)中)、一些首选项(头像图像的 URL)等。初始化时 MyUserService还准备数据(例如从远程加载)。这将用于多个独立的屏幕/ View Controller ,而不是单例。

现在每个有兴趣访问这些数据的 View Controller 都有一个简单的属性:

lazy var userService: UserService = MyUserService()

我将它公开是因为这允许我在单元测试中轻松模拟/ stub 它(如果我需要这样做,我可以创建一个模拟/ stub 行为的虚拟 TestUserService)。实例化也可以是一个闭包,如果 init 需要参数,我可以在测试期间轻松切换它。显然,这些属性甚至不一定需要是 lazy取决于对象的实际作用。如果提前实例化对象没有坏处(记住单元测试,还有传出连接),只需跳过 lazy .

绝招显然是到设计UserService和/或 MyUserService以在创建它的多个实例时不会导致问题的方式 .但是,我发现这在 90% 的情况下并不是真正的问题,只要实例应该依赖的实际数据保存在其他地方,就一个事实而言,例如钥匙串(keychain),核心数据堆栈,用户默认值,或远程后端。

我知道这是一种逃避的答案,因为在某种程度上我只是在说描述一种方法(至少是其中的一部分)许多通用模式。然而,我发现这是在 Swift 中处理依赖注入(inject)的最通用和最简单的形式。协调器模式可以与其正交使用,但我发现它在日常使用中不太“像苹果”。它确实解决了一个问题,但大多数情况下,您没有按照预期正确使用 Storyboard(尤其是:只是将它们用作“VC 存储库”,从那里实例化它们并在代码中转换自己)。

[1] 除了一些基本的和/或次要的东西,你可以传入一个完成处理程序或 prepareForSegue .这是有争议的,取决于您遵循协调员或其他模式的严格程度。就个人而言,我有时会在这里走捷径,只要它不会使事情变得臃肿和变得凌乱。一些弹出式设计通过这种方式更简单。

作为结束语,“请注意,我们希望避免使用单例模式”以及您对此问题下的评论给我的印象是您只是遵循了该建议,而没有正确考虑其基本原理。我知道“Singleton”经常被认为是一种反模式,但这种判断也经常被误判。单例可以是一个有效的架构概念(您可以从它在框架和库中广泛使用的事实中看出这一点)。它的坏处只是它经常诱使开发人员在设计中走捷径并将其滥用为一种“对象存储库”,这样他们就不需要考虑何时何地实例化对象。这会导致模式困惑和声誉不佳。

一个 UserService ,取决于它在您的应用程序中实际执行的操作可能是单例的理想选择。我个人的经验法则是:“如果它管理某种独特而独特的东西的状态,比如在给定时间只能处于一种状态的特定用户”,我可能会选择单例。

特别是如果你不能按照我上面概述的方式设计它,即如果你需要内存中的单一状态数据,单例基本上是一种简单而正确的实现方式。 (即使使用(惰性)属性是有益的,您的 View Controller 甚至不需要知道它是否是单例,您仍然可以单独 stub /模拟它(即不仅仅是全局实例)。)

关于ios - 快速依赖注入(inject),具有两个 UIViewController 的依赖关系图,没有共同的父级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53560257/

有关ios - 快速依赖注入(inject),具有两个 UIViewController 的依赖关系图,没有共同的父级的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  3. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  4. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  5. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  6. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  7. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  8. ruby-on-rails - Rails 3.1 中具有相同形式的多个模型? - 2

    我正在使用Rails3.1并在一个论坛上工作。我有一个名为Topic的模型,每个模型都有许多Post。当用户创建新主题时,他们也应该创建第一个Post。但是,我不确定如何以相同的形式执行此操作。这是我的代码:classTopic:destroyaccepts_nested_attributes_for:postsvalidates_presence_of:titleendclassPost...但这似乎不起作用。有什么想法吗?谢谢! 最佳答案 @Pablo的回答似乎有你需要的一切。但更具体地说...首先改变你View中的这一行对此#

  9. ruby - Rails 关联 - 同一个类的多个 has_one 关系 - 2

    我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下

  10. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

随机推荐