草庐IT

android - 尝试在 iOS 应用程序上安装 "clean architecture"

coder 2023-07-27 原文

最近我一直在反射(reflection)我的 android architecture project ,试图让它适应更“干净的架构”,特别是那种设计 suggested by “Uncle Bob” .

它涉及多个抽象层,很好的职责隔离和通过依赖注入(inject)实现的非常强的依赖倒置;这最终导致了一个非常解耦的可移植系统。通过单元测试和集成测试进行测试的完美候选者。

在我的 android 实现中,我最终拥有三个不同的模块或层:

- :实体、交互者、演示者 (纯java模块)

- 数据 :(充当向域提供数据的存储库) (android 库模块)

- 演示文稿 : ui 相关的东西、 fragment 、 Activity 、 View 等 (安卓应用模块)

所以,我试图找出 iOS 生态系统中最好的方法。
我尝试创建一个具有多个目标的项目来实现相同的解决方案:

- :命令行目标 (这看起来很奇怪,但我认为这是可用的最纯粹的 swift 目标)

- 数据 : cocoa touch 框架

- 演示文稿 : cocoa touch 框架

通过这种方法,我可以像使用 android 模块一样使用这些目标。但是我发现的第一个警告是我需要手动将每个新文件添加到依赖目标。

但是我对具有多个目标的项目的了解非常有限。我的意思是我从未创建过具有多个目标的 iOS 应用程序。所以我不知道该解决方案是否会使用框架(cocoa touch/cocoa)作为目标,而不是域层的命令行模块。

任何想法都会非常感激。

谢谢!

最佳答案

Uncle Bob 的 Clean Architecture 绝对适用于 iOS、Swift 和 Obj-C。架构与语言无关。 Bob 叔叔自己主要用 Java 编写代码,但在他的演讲中他很少提到 Java。他的所有幻灯片甚至都没有显示任何代码。它是一种适用于任何项目的架构。

为什么我这么确定?因为我已经研究了 2 年的 MVC、MVVM、ReactiveCocoa 和 Clean Architecture。到目前为止,我最喜欢 Clean Architecture。我通过将 7 个 Apple 示例项目转换为使用 Clean Architecture 来测试它。我已经专门使用这种方法一年多了。每次都效果更好。

一些好处是:

  • 更快、更轻松地查找和修复错误。
  • 将业务逻辑从 View Controller 提取到交互器中。
  • 将 View Controller 中的展示逻辑提取到展示器中。
  • 通过快速且可维护的单元测试,自信地改变现有行为。
  • 编写具有单一职责的较短方法。
  • 以明确的既定边界解耦类依赖关系。

  • 我们还添加了一个路由器组件,以便我们可以使用多个 Storyboard。没有更多的冲突。

    编写单元测试也大大简化了,因为我只需要在边界处测试方法。我不需要测试私有(private)方法。最重要的是,我什至不需要任何模拟框架,因为编写自己的模拟和 stub 变得微不足道。

    我在 Clean Swift 上写了我过去 2 年学习 iOS 架构的经验。我还将一些 Xcode 模板放在一起以生成所有 Clean Architecture 组件以节省大量时间。

    更新 - 在下面的评论中回答@Víctor Albertos 关于依赖注入(inject)的问题。

    这是一个非常好的问题,需要很长的详细答案。

    始终保留 VIP周期心里。在这种情况下,doSomethingOnLoad()方法不是边界方法。相反,它是一个仅在 CreateOrderViewController 内调用的内部方法。 .在单元测试中,我们测试单元的预期行为。我们给出输入,观察输出,然后将输出与我们的期望进行比较。

    是的,我本来可以做 doSomethingOnLoad()一种私有(private)方法。但我选择不这样做。 Swift 的目标之一是让开发人员更容易编写代码。所有边界方法都已在输入和输出协议(protocol)中列出。真的没有必要用无关的私有(private)修饰符乱扔类。

    现在,我们确实需要以某种方式测试“CreateOrderViewController 应该使用此请求数据在加载时执行某些操作”的行为,对吗?如果我们不能调用 doSomethingOnLoad() 我们如何测试这个因为它是一个私有(private)方法?您拨打 viewDidLoad() . viewDidLoad()方法是一种边界方法。哪个境界?用户和 View Controller 之间的边界!用户对设备做了一些事情以使其加载另一个屏幕。那么我们如何调用viewDidLoad()然后?你这样做:
    let bundle = NSBundle(forClass: self.dynamicType)
    let storyboard = UIStoryboard(name: "Main", bundle: bundle)
    let createOrderViewController = storyboard.instantiateViewControllerWithIdentifier("CreateOrderViewController") as! CreateOrderViewController
    let view = createOrderViewController.view
    

    只需拨打 createOrderViewController.view属性会导致 viewDidLoad()被调用。我很久以前从某人那里学到了这个技巧。但是Natasha The Robot最近也提到过。

    当我们决定测试什么时,只测试边界方法是非常重要的。如果我们测试一个类的每一个方法,测试就会变得非常脆弱。我们对代码所做的每一次更改都会破坏很多很多测试。很多人因此而放弃。

    或者,这样想。当你问如何模拟时 CreateOrderRequest ,先问是否doSomethingOnLoad()是您应该为其编写测试的边界方法。如果不是,那是什么?边界法其实是viewDidLoad()在这种情况下。输入是“当这个 View 加载时”。输出是“用这个请求对象调用这个方法”。

    这是使用 Clean Swift 的另一个好处。所有边界方法都列在文件顶部明确命名的协议(protocol) CreateOrderViewControllerInput 下。和 CreateOrderViewControllerOutput .你不需要去别处寻找!

    想想如果你要测试会发生什么doSomethingOnLoad() .您模拟请求对象,然后断言它等于您预期的请求对象。你在 mock 某事并比较它。就像 assert(1, 1)而不是 var a=1; assert(a, 1) .重点是什么?太许多 测试。太易碎 .

    现在,有一段时间你可以模拟 CreateOrderRequest .在您验证正确后 CreateOrderRequest可以由 View Controller 组件生成。测试时 CreateOrderInteractordoSomething()边界方法,然后你模拟 CreateOrderRequest使用接口(interface)依赖注入(inject)。

    总之,单元测试不是测试类的每个单元。它是关于将类作为一个单元进行测试。

    这是一种思维方式的转变。

    希望有帮助!

    我在 Wordpress 中有 3 个关于不同主题的草稿系列:
  • 深入了解每个 Clean Swift 组件
  • 如何将复杂的业务逻辑分解为 worker 和服务对象。
  • 在 Clean Swift iOS 架构中编写测试

  • 你最想先听听哪一个?我应该在测试中增加系列吗?

    关于android - 尝试在 iOS 应用程序上安装 "clean architecture",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32201661/

    有关android - 尝试在 iOS 应用程序上安装 "clean architecture"的更多相关文章

    1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

      我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

    2. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

      我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

    3. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

      我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

    4. ruby - 将差异补丁应用于字符串/文件 - 2

      对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

    5. ruby - ECONNRESET (Whois::ConnectionError) - 尝试在 Ruby 中查询 Whois 时出错 - 2

      我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.

    6. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

      我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

    7. ruby - 如何指定 Rack 处理程序 - 2

      Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

    8. ruby - 在 Ruby 中编写命令行实用程序 - 2

      我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

    9. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

      为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

    10. ruby - 完全离线安装RVM - 2

      我打算为ruby​​脚本创建一个安装程序,但我希望能够确保机器安装了RVM。有没有一种方法可以完全离线安装RVM并且不引人注目(通过不引人注目,就像创建一个可以做所有事情的脚本而不是要求用户向他们的bash_profile或bashrc添加一些东西)我不是要脚本本身,只是一个关于如何走这条路的快速指针(如果可能的话)。我们还研究了这个很有帮助的问题:RVM-isthereawayforsimpleofflineinstall?但有点误导,因为答案只向我们展示了如何离线在RVM中安装ruby。我们需要能够离线安装RVM本身,并查看脚本https://raw.github.com/wayn

    随机推荐