Tag: 设计模式

iOS-SOLID原则第4页-接口隔离原则

接口隔离原理说: “许多特定于客户端的接口比一个通用接口要好。” 在iOS中,该主题中的Interface可以转换为Protocol。 它说,许多协议要比具有一个通用目的的一个大协议(或类,结构或枚举)更好。 客户特定协议将是某些客户只需要的协议。 假设您有两个课程: 猫与人 : 类Human {func work(){} func sleep(){} func GiveFood()-> Food {return Food()} //其他功能} class Cat {var owner:Human var foodBowl:[Food] = [] func requestFood(){print(“ Meow,gimme food”)let foodScoop = self.owner.feedPet()self.foodBowl.append(foodScoop)} func eat(){self.foodBowl.removeAll()} init(owner:Human){self.owner = owner}} 如您所见, Cat是Human的客户。 它消耗人类的数据。 猫不能告诉它的主人去工作,也不需要知道那件事。 在此示例中,所有者需要的只是食物。 这就是隔离的来历。 正如我们所说,猫只在乎他的食物。 Cat没有理由暴露于类似于work()或sleep()之类的人类功能。 所以我们可以声明一个PetFoodProvider协议: 协议FoodProvider {func feedPet()-> Food} 因此,我们的客户( Cat )将仅使用(并将暴露给它)所需的东西: […]

在Swift中使用通道进行数据流📻

最初发表在 Swift Post上 。 Apple框架大量使用委托和观察者模式(NotificationCenter)来传递信息。 尽管这些模式没有错,但实际的实现始终对我来说有点矛盾。 首先让我们看一下这些模式的基本特征: 委派:支持一对一的双向通讯。 观察者 :支持一对多,单向通信。 让我们看一些UITableViewDelegate方法。 可选的func tableView(_ tableView:UITableView,heightForRowAt indexPath:IndexPath)-> CGFloat UITableView :发件人 UITableViewDelegate (主要是 UIViewController ) :接收器 此方法是一对一双向通信的一个很好的例子。 由于表格视图要求控制器返回高度,因此通讯是双向的,不能一对多。 (否则,我们将无法决定应使用哪个返回的高度值。) 但是,以下方法不返回任何内容。 可选的func tableView(_ tableView:UITableView,didSelectRowAt indexPath:IndexPath) 在此,发送方仅通知接收方。 因此,通信是单向的,可以是一对一或一对多。 在这种情况下,授权是一个不错的选择吗? –也许… 如果我们需要通知多个组件会怎样? –我们不能。 如果我们不在乎行选择,而是想自己提供行高怎么办? 我们还需要提供空的实现吗? –如果这是一个纯Swift协议, 是的 ,我们将需要为每个我们不关心的委托方法准备空的实现,这很烦人。 由于UITableViewDelegate继承自Objective-C,因此为了向后兼容,我们可以将方法标记为可选。 但是Swift出于某种原因禁用了此“功能”。 具有庞大的协议会违反接口隔离原则,并使它们无法读取。 表视图是我作为开发人员生活的重要部分,但我仍然不记得UITableViewDelegate和UITableViewDataSource协议中定义的内容,因为列表太长了。 那么……为什么我们不对每个单向动作都使用观察者模式呢? 我们可以提供具有更大灵活性的相同功能,并且还可以清除大型委托协议中的大多数方法。 我们有一个设置页面,其中包含主题选择。 每当主题更改时,我们都希望更新主屏幕。 解决方案1:使用委派 此解决方案有效,但是否足够灵活? 如果我们有5个选项卡要与此主题更改进行更新,会发生什么情况? 解决方案2:使用NotificationCenter 现在,我们支持一对多通信,但是我们将线路数量增加了一倍,对吗? […]

Swift中的UI测试

一,为什么要使用UI测试? 1.自动化手动测试🤖 测试应用程序时,我们总是重复相同的过程以测试我们没有破坏任何东西。 我们可以通过一系列与手动测试相同的测试来实现这一目标。 2.表示层中的逻辑丰富🌅 我们拥有的UI逻辑越多,我们测试的内容就越多 3.节省时间⌚️ 有了一系列的ui测试,我们可以在更少的时间内每次都重复相同的手动测试。 二。 我们可以测试什么? 纽扣 刷新表格 输入文字 滑杆 检查导航栏标题 推拉视图控制器 重新排列细胞 等等.. 您可以查看此UI测试备忘单以获取更多信息。 三, 我们如何正确测试? 1.有助于将逻辑与表示法separate分开的架构。 当我们谈论MVC时,我们都知道它不是非常可测试的,因为视图和控制器之间的交互非常紧密。 我们可以使用MVVM或VIPER架构,通过更好的分布来实现更好的可测试性。 您可以查看这篇很棒的文章,以获得更多信息iOS体系结构模式。 2.依赖注入💉 这种软件设计模式允许在运行时而不是在编译时选择组件,目的是用Test Doubles代替生产代码。 依赖注入的示例将类似于以下ServiceLocator: CharactersViewController :是主视图控制器,并显示GOT字符列表。 CharacterDetailVC :显示CharacterDetailVC的详细信息。 字符VC测试 辅助方法 我们将在测试中使用一些辅助方法。 我们将不再需要手动重复此过程this。 五,从这里去哪里 在github上检查整个项目,查看所有使用的配置和框架。 尝试自己编写一些UI测试。 创建您自己的项目,并按照此结构做一些事情! VI。 归因 GOT Challenge Swift应用程序基于Idealista GOT Challenge Android。

IOS中的VIP(干净的Swift架构)

VIP还鼓励使用模板“促进”其实施。您可以在Clean Swift IOS体系结构上找到模板,以及有关VIP生命周期的更多详细信息。 使用这些模板,您无需担心项目中VIP架构的实现。 模板有助于轻松编写测试用例。 VIP单向方式 VIPER双向方法 在上图中,我们可以看到VIP架构采用的是单向方法,与VIPER有所不同,后者遵循的是双向方法。这两种架构均基于Single Responsibility Principle(单一责任原则),可带来整洁的架构和更好的可测试性。 如何在项目中使用VIP模板 首先从干净的Swift架构下载模板 将干净的swift文件夹复制到/ Users / / Library / Developer / Xcode / Templates / File \ Templates 启动Xcode并创建一个新文件(文件>新建>文件或⌘N) 搜索Clean Swift部分,然后在可用模板之间进行选择 这样,您就可以轻松地在项目中实施VIP架构。 同样,VIP模板具有用于单元测试实施的模板。 众所周知,VIP完全支持TDD(测试驱动开发)。您不必担心为项目编写单元测试类。 VIP演示项目 如果您对它在现实生活项目中的实施更加困惑,我在Github项目中对VIP架构做了简单的演示。 关于项目中的实施以及单元测试的实施,以便您可以开始遵循项目中的TDD(测试驱动开发)。 VIP架构实施 VIP不仅由三部分组成,尽管其名称仅是VIP,但它不仅具有ViewControllers,Interactors和Presenters等更多的组件,例如数据模型,路由器和Workers,而且可以将Viewcontrollers划分为Configurator。变得庞大。 ViewController 在ViewController的viewDidLoad() ,我们需要运行一些业务逻辑,因此我们将其称为fetchPosts() 。 在fetchPosts() ,我们在输出(交互器)上调用fetchPosts(request) )。 而已。 我们要求输出执行我们的业务逻辑。 视图控制器不需要也不应该关心谁以及如何完成。但是,如果有必要,可以将ViewController划分为Configurator。 互动者 交互器包含您应用程序的业务逻辑。 用户在您的UI中点击并滑动即可与您的应用进行交互。 视图控制器从UI收集用户输入,并将其传递给交互器。 然后,它检索一些模型,并要求一些工人进行这项工作。 工人 帖子视图可能需要从Core […]

iOS — SOLID原则第3页– Liskov替代原则

Liskov替换原理非常简单。 它指出: “程序中的对象应该可以用其子类型的实例替换,而不会改变程序的正确性。” 本质上,在iOS中,这意味着您应该以一种方式对类/协议进行建模,以便可以将符合该协议的任何子类/类型替换为子类型,而不会改变程序的正确性。 例如,您有一个MyClass类。 MyClass使用另一个模块执行任务,我们称它为OperationExecuter 。 OperationExecuter可以是协议或类。 您有一个新的子类型,该子类型可以子类化/符合OperationExecuter ,我们称其为FasterOperationExecuter 。 您可以在MyClass中替换FasterOperationExecuter (如果我们通过依赖注入使用OCP的话),而无需更改其正确性。 以第2部分(OCP)中的最后一个示例为例: 并给出替换示例,也来自上一部分: let imageStorage = NoirImageStorage(访问器:RemoteServerFilesAccessor()) 我们已经完成了LSP。 如果现在将imageStorage属性更改为使用NoirImageStorage ,则所有操作仍将相同。 LSP就是这样。 我们还将ImageStorage使用的FileAccessor更改为RemoteServerFilesAccessor ,而不是DocumentDirectoryFileAccessor 。 我们的imageStorage仍然按照其需要执行操作,只是有所不同。 它仍然会拍摄图像并将其存储在我们的存储层中 (除了现在也使用服务层)。 在第一个示例中,它拍摄一张图像并将其保存到文档目录中。 在第二个示例中,它将拍摄图像,对其应用Noir效果,然后将其存储在远程服务器中。 我们没有更改ProfilePicManager的正确性。 它仍然执行需要完成的任务(只是有所不同),我们没有改变它的正确性。 转到第4部分- 接口隔离原理。

Swift中的Singleton模式

Singleton可以说是编程世界中使用最广泛的设计模式。 有些人说不使用它们,而有些人则有不同的看法,但我们都知道它们在编程时有多方便。 好吧,这篇文章不是关于Singleton的优缺点,也不是我们是否应该使用它们? 关于如何在iOS(Swift)中正确使用它们,尤其是在多线程环境中。 因此,让我们开始吧– 单例模式可确保在应用程序的整个生命周期中仅实例化一个类的实例。 有时您要确保仅实例化一个类的实例,并且您的应用程序仅使用该实例。 那是单例模式的主要目标。 例如,我们经常在iOS中使用Singleton – UserDefaults //最广泛用于持久性 UserNotifications //用于远程通知(现在或之后使用) UIApplication //主应用程序类 SKPaymentQueue //应用内购买付款队列(StoreKit) URLSession //用于网络通话 NSNotificationCentre //用于广播 FileManager //用于文件I / O操作 这些都是我们在编程时很容易使用的iOS中的Singleton示例。 让我们看看如何在iOS(Swift)中创建自己的Singleton 这就是我们快速创建Singleton模式的方式,过去我们不得不将实例化代码放置在dispatch_once块下才能执行一次的日子已经过去了,这样我们可以保证对象实例化只发生一次,静态常数,我们无需做任何特殊的事情就可以实现。 苹果保证所有静态常量仅实例化一次,并且它们隐式使用dispatch_once,证明在这里– 现在,如果您仔细观察,就会发现我们的代码中还有另外一件事,是的,我们正在谈论私有初始化程序。 为什么在那儿? 将初始化器设为私有需要什么? 因此,您的问题的答案是– 如果别人这样做 让anotherInstance = Singleton(); //但是,我们说过只能有一个此类的实例,但是我们不能阻止它们像上面那样创建另一个实例,因此这就是为什么我们将初始化程序设为私有的原因。 现在,如果我们尝试创建另一个对象,则编译器将给我们错误– 到目前为止,上面的代码没有问题,它可以完美运行,我们创建了Singleton,并且我们将初始化程序设为私有,以避免创建更多对象,但这是Singleton对象的唯一实例。当我们的应用程序会话未使用Singleton对象但它存在于内存中时,就会出现这种情况。 因此,要解决此问题-我们可以延迟实例化该实例吗? 在多线程环境中会不会有问题? 如果是,那么如何解决呢? 让我们来看看 – 因此,请考虑以下情形:线程A试图实例化一个对象,而在实例化另一个线程B之前也请求该对象,因此为了避免这种情况(很容易在多线程环境中出现),我们提出了一个障碍,不允许稍后的线程(s)进入初始化部分,因此只有一个实例被初始化。 继续阅读,继续分享🙂

测试苹果MVC

苹果公司针对iOS应用程序的官方主要设计模式是模型视图控制器。 该模式包含一个模型(包含我们的数据),一个视图(显示数据)和一个控制器(将所有内容粘合在一起)。 从他们的角度来看,一切都可以归为这三个组成部分之一。 因此,让我们看看如何测试每个组件。 模型 该模型包含工作所需的数据。 不过要小心; 它不包含仅与视图相关的任何数据,例如按钮显示的文本。 相反,它可能是核心数据存储或游戏系统。 这意味着我们已经知道如何测试模型。 模型公开了一个api(方法,属性等),并且不需要任何额外的工作来设置测试。 视图 对于视图,我们必须假设Apple负责将物理交互转换为数据并将其传输到屏幕/视图的正确位置。 有了这个假设,我们可以创建我们要测试的两种视图数据: 静态的 更换零件 我们可以通过创建屏幕快照来测试静态部件,然后将当前屏幕快照与此进行比较,但老实说,这对于UX来说是有用的。 作为开发人员,我认为只要专注于视图的不断变化的部分和交互作用就可以了。 有趣的是,这导致我们必须测试控制器,因为它正在传达交互作用。 控制者 让我们开始研究应用程序的功能。 在iOS中,它称为ViewController。 该控制器接受事件,控制逻辑并使我们的应用程序正常工作。 所有这些功能都留下了“难以测试”的印象。 至少它对我有用。 如果您认为iOS具有两种不同类型的InterfaceBuilder创建视图,它将变得更加强大。 那么为什么它似乎很难测试? 它做的很多,这意味着要进行大量的测试和进行交互的各个部分。 如何测试视图生命周期的方法? 特定于视图的方法,例如tableView(tableView:didSelectRowAt 🙂 如何测试IBAction? 如何测试IBOutlets? 让我们解决所有这些问题。 它做很多 听起来很糟糕,确实如此。 在大多数情况下,您需要考虑重构代码。 如果您无法减少代码中的功能量,它仍然不会阻止您进行测试。 一开始只是令人恐惧,您可能想在此过程中进行重构。 如何测试视图生命周期的方法? 有两种意见。 一种是您以与其他所有操作相同的方式来称呼它们。 只要您没有引用IBOutlets的代码,您甚至都可以自己经历整个生命周期。 另一个选择是从这些函数中提取所有代码,然后调用它们。 这样,您始终可以确保小型逻辑代码片段能够按预期的方式运行。 因此,这与我们在其他地方所做的相同。 特定于视图的方法 听起来很奇怪,但最终它们只是功能。 在这种情况下,它们是公共的,因此您不必创建类别并添加它们。 IB动作和IBOutlets IBActions只是方法。 给他们打电话! 另一方面,IBOutlets需要ViewController来扩大视图并设置所有Outlets。 一旦完成,就可以使用与属性相同的方式进行测试。 所以问题是:如何为视图充气? […]

教程设计模式-OOP的力量(第1部分)

先决条件—本博客系列需要具有面向对象编程的中级专业知识。 您应该具有有关类,对象,构造函数,继承,值和引用类型的基本知识。 通过从头到尾阅读本系列文章,中级者将获得知识,专家将增强其知识。 设计模式用于表示经验丰富的面向对象的软件开发人员社区所采用的最佳实践。 生成器设计模式可帮助我们以更简单易读的方式构建对象。 生成器设计模式遵循以下两个简单规则。 分开原始的类表示形式及其构造方法。 在最后一步返回类的实例 问题: 考虑一下类,说Person具有十个或更多属性,当您需要创建Person类的实例时,对它的构造函数进行设计。 它的构造函数将使用十个或更多的参数,很难将多个参数作为单个函数或构造函数进行管理,最终,您将失去代码的可读性。 查看以下示例。 尝试在操场上运行上面的示例,它将成功运行,并为您提供预期的输出。 从逻辑上讲这是正确的。 我们可以通过克服以下几点来改进上面的示例。 我们必须以提到的顺序传递值,不能重新排列参数序列以提高可读性。 即使在对象创建时不知道某些值,我们也必须传递所有值。 例如,假设您需要创建一个Person类的对象,但是此人仍在寻找工作。 当该人加入任何公司时,只有我们会提供作品详细信息。 解: 创建相关属性的逻辑组。 为不同的属性组创建单独的构建器类。 在最后一步中检索实例。 让我们用一个例子简化一下, 在示例WithoutDesignPatternExample1.swift中,我们已经有一个名为Person的类,其中有14个属性。 如果我们仔细检查所有14个属性,则这些属性具有4个逻辑组。 个人详细信息属性 联系人详细信息属性 地址详细信息属性 公司详细信息属性 多面设计和流畅设计模式共同帮助我们克服了上述两个问题。 在上面的示例中,我们将Person类的职责划分为不同的类。 现在, Person类仅包含数据属性,而我们创建了多个构建器类,它们负责构建/更新相对的属性组。 我们有一个基本的构建器类PersonBuilder ,还有四个派生的构建器类,分别为PersonPersonalDetailsBuilder , PersonContactDetailsBuilder , PersonAddressDetailsBuilder和PersonCompanyDetailsBuilder 。 基类PersonBuilder帮助我们随时在多个构建器之间切换,而派生自PersonBuilder的其他四个构建器负责更新相对属性。 在上面的示例中,我们可以清楚地看到,与第一个示例WithoutDesignPatternExample1.swift相比, Person对象的构造更具可读性,而且我们可以随时以更易读的方式更新属性组或任何单个属性。 在上面的示例中,请注意,在调用每个属性更新方法后,我们将返回生成器实例的自身。 这有助于我们编写同一构建器的多个方法的链接,而不是分别编写多行。 这个概念被称为流畅模式。 好处: 以更易读的方式轻松初始化具有太多属性的类的对象。 遵循单一责任原则。 根据您的方便,以任何顺序初始化对象或更新属性。

Swift解决方案:装饰器模式

Swift Solutions是涵盖设计模式的一系列文章。 在每篇文章中,我们讨论该模式是什么,何时应用以及如何以Swifty方式实现它。 在Swift解决方案的此迭代中,我们将在Swift中实现Decorator Pattern。 该模式提供了一种向应用程序添加功能的有用方法,并且是每个开发人员都可以在其套件中使用的出色工具。 我们将在方法上做到周密,以免材料发粘。 我们将在Swift中定义,说明和编码模式。 最终,您将了解该模式是什么,何时应用以及如何编码。 让我们直接进入定义! 限定 装饰器模式可动态修改核心对象的行为,而无需更改其现有的类定义。 装饰器模式有两个主要组成部分:一个将修改其行为的核心对象,以及一个在核心对象中带来更改的行为的装饰器对象。 这两个组件可以共同实现两个主要目标: 行为修改 …动态实现 行为修改 该模式的目的是在不修改其现有类实现的情况下更改核心对象的工作方式。 这是通过用装饰器包装核心对象来实现的,装饰器可以增强核心对象的默认行为。 装饰器从字面上完全“装饰”(修改)了它们包装的对象。 装饰器增强了核心对象中已经存在的行为。 它们与核心对象共享相同的接口,但提供不同的实现。 结果,客户端无法区分是使用装饰器还是使用核心对象:它们在两者上调用完全相同的函数/属性。 此共享接口允许装饰器充当中间人,拦截对核心对象的调用并为客户端提供自定义行为。 动态修改 装饰器不只是简单地修改行为,而是动态地修改行为。 换句话说,它们更改了对象在运行时(而不是编译时)的工作方式。 编译时行为修改很简单:只需要​​直接更改对象的类定义或子类并覆盖其默认实现即可。 但是,由于以下原因,可能不希望使用这些选项: 可以将核心对象类声明为final ,这可以防止子类化。 该类可以在第三方框架中定义,对它的任何更改都将在下一次框架更新时丢失。 可能存在许多所需的修改,并且可以将几种行为链接在一起以产生复杂的组合。 为每种可能的组合创建一个子类将导致类爆炸。 对现有类的更改可能会导致意外的副作用,从功能中断到崩溃。 当继承脆弱且未完全理解的旧版代码库时,尤其如此。 所有这些缺点使动态方法成为有吸引力的替代方法。 我们没有将具有不同行为的核心对象的多个变体硬编码,而是将每个行为分成了自己的专用包装器类。 然后,通常在用户输入期间,将这些专用包装器在运行时应用于核心对象。 用户选择他们的自定义项,然后我们应用包装器以产生所需的结果。 这是一种灵活的方法,因为行为的组合是通过根据需要增量应用包装器来实现的。 说明 我们将使用赛车游戏作为插图。 在此游戏中,玩家在每次比赛前选择车辆。 选择后,他们可以自定义车辆的轮胎,变速箱,发动机等。 这些自定义通过更改其统计信息来影响车辆的行驶方式。 在我们的示例中,统计信息将是速度和牵引力。 玩家可以选择更换零件以更好地适应他们的喜好。 例如,玩家可以选择将默认轮胎更改为越野轮胎,这些轮胎的抓地力更好,但速度却有所降低。 实行 让我们使用赛车插图来实现该模式。 protocol Transporting { func […]

Swift中的项目架构MVVM

如果您一直在使用Model-View-Controller模式来构建Swift应用程序,这可能看起来很熟悉。 它代表Model-View-ViewModel 问题: 让我们从一个例子开始: 在上述程序中, MovieViewController完成加载其视图后,将执行网络请求。 它将此任务委托给MobileAPIService类,这是减少视图控制器职责的良好起点。 实现看起来不太复杂,这可能是正确的。 它有一些缺陷。 但是我们关心的是视图控制器为何负责发出网络请求?🤔确实, MobileAPIService可以帮助MovieViewController完成此任务,但是MovieViewController仍然知道它获取的电影来自后端。 您可能会争辩说,这些细节被MobileAPIService隐藏了,但这不是重点。 对于MovieViewController应该考虑以下几点 。 MovieViewController控制一个视图。 除了API调用,本地数据库处理等其他处理外,它仅应用于UI呈现。 它不应该知道电影从哪里来。 它只需要询问要在其视图中显示的内容。 它甚至不应该了解MobileAPIService。 更不用说它是如何工作的。 在客户端程序(我们的案例MovieViewController )中,必须有最少的代码,从而减少了错误。 不幸的是,我们没有在程序1.0中实现以上几点。 解决方案: MVVM模式是MVC模式的稍微复杂一些的实现,请花一点时间来思考一下MVVM在上述实现中可能具有的优势。 与程序1.1相比,在程序1.0中的实现是如此简单,这可能是正确的。 MVC实现的ViewController还可以管理状态,newsList实例,MobileAPIService等。 这不是坏事还是好事,但是在MVVM中,它在ViewMode中隐藏了这些细节 MVVM实施 的 新变化: MVVM模式在ViewModel中隐藏电影,状态, MobileAPIService详细信息。 发现ViewController仅负责UI控件。 ViewController与一系列电影无关。 甚至不知道电影的来源,例如通过远程本地存储。 它们可能来自由核心数据SQLite管理的后端或本地存储。 单元测试和可测试性得到改善。 它将这些细节隐藏在ViewModel中 是什么使Model-View-ViewModel很棒 体验MVVM的第一个好处是可以从视图控制器中删除多少代码。 视图模型是位于模型层和控制器层之间的轻型对象。 您的视图控制器不会直接与应用程序的模型层通信。 视图模型为视图控制器提供数据,并提供与模型层交互的界面。 单元测试 通过移动业务逻辑(数据操纵)来查看模型。 测试我们的应用程序更加容易。 测试视图模型很容易,因为它不包含客户端程序引用。 视图控制器不依赖于模型层,因此易于测试。 任务分离 通过添加视图模型,视图模型将数据层的数据转换为视图使用的某种内容(例如模型)。 视图控制器不负责此任务管理。 请找到完整的源代码 shantaramk / […]