Tag: Solid

将SOLID应用于UIApplicationDelegate

作为iOS开发人员,您一定熟悉AppDelegate类及其遵循的协议。 那是一切*开始并有时结束的地方。 App委托托管了大量有用的方法和回调。 与您应用的生命周期,后台传输,后台获取或连续性相关的所有内容。 更不用说用户和远程通知了。 确实功能强大,但是……对于一个很小的班级来说,这不是太多吗? 那么单一负责原则呢? 它只有一个改变的理由吗? 当您的应用程序增长时,您会添加更多服务,例如社交登录,分析,崩溃报告。 所有这些好东西都有它们自己的库,当然需要在AppDelegate中进行设置。 这就是您的代表开始看起来和闻起来像一些旧的意大利细面条代码的地步。 对于我的一些旧AppDelegates增长了多少行,我将不为您提供令人尴尬的细节。 不要误会我的意思。 Apple Cocoa上存在的代表模式非常好,而且一定会成功。 但是,这是一对一的关系,显然不适合这种情况。 如何将代表变成更适合这份工作的代表? 这就是我最喜欢的一对多设计模式之一,即观察员。 让我们尝试一下。 将此数组作为实例变量添加到您的AppDelegate。 私人 var观察者:[UIApplicationDelegate]? 现在,在需要转发给观察者的每个委托方法中,添加此简单循环。 func 应用程序 ( _应用程序:UIApplication, didFinishLaunchingWithOptions launchOptions:[UIApplicationLaunchOptionsKey:Any]?)->布尔{ 观察者?.forEach { _ = $ 0.application?(应用程序,didFinishLaunchingWithOptions:launchOptions) } 返回 真 } 而且…就是这样。 您(可能)永远不会再触摸AppDelegate。 太好了,但是我需要在AppDelegate中设置的新炫酷服务呢? 让我们设置Firebase。 导入UIKit 导入Firebase FirebaseObserver类 :NSObject,UIApplicationDelegate { func应用程序 (_应用程序:UIApplication, didFinishLaunchingWithOptions launchOptions:[UIApplicationLaunchOptionsKey:任何]? = nil)->布尔{ FirebaseApp.configure() […]

iOS-SOLID原则第1页-SRP

SOLID原则是5个OOP设计原则,可帮助您使软件更加灵活,可读性和可维护性。 在这个由五部分组成的系列文章中,我将介绍其中的每一个,并通过示例介绍其中的每一个如何为您提供帮助。 S —单一责任原则(或简称为SRP) 一个模块应该只有一个责任,只有一个改变的理由。 O —打开/关闭原理(OCP) 应该打开一个模块进行扩展,但关闭该模块进行修改。 L — Liskov替换原理(LSP) 程序中的对象应该可以用其子类型的实例替换,而不会改变该程序的正确性。 I —接口隔离原理(ISP) 许多特定于客户端的接口比一个通用接口要好。 D-依赖倒置原则(DIP) 一个人应该“依靠抽象而不是凝结。” 在第一部分中,我将介绍第一个原理SRP。 我认为,这是iOS开发中最重要的5个。 “一个模块应该只有一个责任,只有一个改变的理由。” 从本质上讲,这意味着代码中的对象,类型,类或任何其他模块应负单一责任,这也是更改的唯一理由。 为了说明这一点,假设您有一个应用程序,用户可以在其中设置个人资料图片。 该图像仅显示给用户,因此可以将其存储在磁盘上。 没有SRP,用于编辑个人资料图片的视图控制器将如下所示: 它不在乎也不应该在意如何将食物带到餐桌上。 唯一的责任就是吃它。 他不需要知道您换了工作,升职或被解雇了。 他根本不在乎您如何工作,只要您将他需要的食物带给他即可。 —唯一要担心的是。 我们的下一步将是分离关注点。 就像上面的猫一样,我们的视图控制器不需要知道默认的配置文件资产名称,也不需要知道如何保存它或保存在哪里。 唯一的责任是在UIImageView上显示UIImage。 现在,我们需要一个将处理所有个人资料图片加载和保存的类。 我们可能最终会得到这样的结果: 因此,视图控制器现在看起来像这样: 如我们所见,将个人资料图片管理与视图控制器分离,可以为我们提供更高的可读性和灵活性。 首先,该类现在可以在其他视图控制器中使用,因为我们将个人资料图片管理职责分离到了另一个模块。 现在也阅读视图控制器会更好。 我们只需将图像从ProfilePicManager加载到将在完成时调用的闭包上,然后显示图像。 认为这种方法比第一种更好,但仍不是SRP。 因为如果我们需要保存的图像不是个人资料图片,还会存储更多图像吗? 在前面的示例中,我们将个人资料图片管理与视图控制器分开。 但是我们的ProfilePicManager没有单一职责。 目前它有两件事: 个人资料图片管理 从存储层获取数据(在这种情况下为文件系统) 从中获取图像或保存图像的方式不是ProfilePicManager的责任或责任。 唯一的工作是从存储层获取个人资料图片,并在用户选择图片时将其交给存储层。 一个模块应该只有一个责任,只有一个改变的理由 。 如果确实要仅更改存储层(例如,从文件系统存储更改为远程服务器存储),而这与ProfilePicManager无关,则也必须更改它。 那不是我们想要的。 在这种情况下,使用SRP,我们只想更改存储层模块。 为了实现这一点,我们将不得不再创建一个类来处理与存储层的通信-ImageStorage 。 […]

iOS-SOLID原则第2页-开放/封闭原则

打开/关闭原理指出模块应该打开以进行扩展,而关闭则可以进行修改。 从本质上讲,在iOS中,这意味着应该通过扩展打开模块,无论是通过快速扩展(或Obj-C类别),子类还是通过依赖项注入(或可以提供的其他任何扩展模块)。 使用扩展模块的任何其他模块仍应能够使用它。 这与SRP原理非常吻合,因为您无需使用该模块更改其他模块中的任何内容。 我将演示第一个示例—在第一部分的ImageStorage类示例中进行子类化。 假设我们要添加一个新的ImageStorage类,它所做的只是保存带有黑色效果的图像。 我们要做的就是子类化ImageStorage并覆盖它的saveImage函数: 如你看到的 我们仍然可以像以前一样使用ImageStorage 。 它没有任何改变,因为我们以对扩展开放的方式编写了ImageStorage 。 它应保持关闭状态以进行修改,因为任何修改都可能使其他模块以我们不希望或期望的方式运行。 对于此示例,我们将修改ImageStorage类的工作方式。 我们将为其添加文件访问器。 从现在开始,所有文件I / O将成为访问者的工作。 (这也将给我们带来更高级别的职责分离。SRP FTW!) 首先,由于我们使用依赖注入,因此ImageStorage现在不能使用静态函数。 接下来,我们需要声明文件访问器协议: 现在,我们需要更改ImageStorage类。 我们将通过init()注入FilesAccessor,从现在开始,加载和保存功能将与FilesAccessor一起使用: 看看SRP效果如何? 现在,ImageStorage不再关心文件访问的责任。 现在,我们可以实现我们的DocumentDirectoryFileAccessor: 最后,我们需要修改ProfilePicManager以使其与具有特定FilesAccessor的特定ImageStorage一起使用 。 例如,我们可以这样做(第9行): 现在,我们可以欣赏代码的灵活性 我们可以走这行: let imageStorage = ImageStorage(访问器:DocumentDirectoryFilesAccessor()) 并替换为: let imageStorage = NoirImageStorage(访问器:RemoteServerFilesAccessor()) 然后,我们会将带有Noir效果的图像存储在服务器上 ( RemoteServerFilesAccessor将与某些服务层模块一起使用)。 视图控制器对此更改一无所知,并且扩展我们的ImagesStorage现在是无限的。 我们可以声明所需的任何访问器,并将它们注入到任何ImageStorage中 ,只要它们符合FileAccessor即可 。 这导致我们进入第3部分: Liskov替代原理。

iOS-SOLID原则第5页-依赖倒置原则

DIP —依赖反转原理,指出: A. 高级模块不应依赖于低级模块。 两者都应依赖 抽象 。 B. 抽象不应依赖细节。 细节应取决于抽象。 我将通过提供一些常用实现示例以及该原理说明的内容来尝试解释该原理。 假设您有一个UITableViewController,可显示附近的蓝牙设备。 我们将其称为NearestDevicesViewController。 VC及其逻辑是高级模块,它使用服务层中的低级模块, BLEClient或类似的东西。 常见的实现方式是NearestDevicesViewController具有BLEClient属性(并取决于BLEClient属性)。 这种方法根本不灵活。 更改BLEClient的工作方式或更改属性以容纳另一个类可能会破坏我们的NearDeviceDevicesViewController 。 更好的方法是使用NearestDevicesViewController需求的抽象。 我们将声明一些协议,例如DevicesProvider 。 该协议将宣布两者之间的“合同”。 视图控制器将持有某种符合该协议的类型的属性,从而使其依赖于抽象(协议),并且任何寻求帮助视图控制器的低层模块都将依赖于实现协议中提到的要求。 请注意,这与Liskov替代原则是很好的,因为此“合同”让我们在不更改程序的情况下,将设备提供程序替换为其他任何子类型) 该原理试图颠覆传统方法,即高级模块依赖于低级模块。 这种方法中的高级模块拥有抽象(通过确定协议的方法),然后这些低级模块就会遵循该抽象。 使较低级别依赖于较高级别模块的要求。 如上所述,这就是低层和高层模块都依赖抽象的地方(在本例中为协议): A. 高级模块不应依赖于低级模块。 两者都应依赖 抽象 。 在DevicesProvider的示例中,我们现在可以最大程度地享受灵活性。 例如,如果现在我们想将服务层BLEClient中的类更改为服务层GPSClient中的另一类,则可以通过依赖项注入来注入它,只要它符合DevicesProvider,并且一切都应该很好。 在这里看看我们如何解耦代码。 而不是这样: 我们现在有这个: 附近的设备视图控制器未与BLEClient耦合。 这意味着我们不必同时使用它们。 每个人都可以替换为另一个人,因为它们现在依赖于抽象而不是彼此依赖。 而已。 感谢您的支持,并在下面发表评论。 如果喜欢就拍手🙂

如何在Swift中应用单一责任原则

有时,当我们学习编码时,很难理解类责任的概念。 这是因为我们的第一个项目变得难以维护,原因是我们的代码中按类包含许多行,更重要的是,它承担了许多责任。 真正了解类责任的一种好方法是考虑可伸缩性。 让我们看一个如何使用它的例子。 但是,在此之前,我想先介绍一些理论并解释为什么这个概念在软件设计中很重要。 每个类在软件项目中负有单个责任的思想,并且将责任封装在一个唯一的类中的想法有一个名称:单一责任原则 这是SOLID软件设计的5条主要原则之一,他们在面向对象编程中尝试定义一个指南,以拥有更易懂,灵活和可维护的软件。 这些原则是: 单一责任原则 开闭原则 里斯科夫替代原则 接口隔离原理 依赖倒置原则 这些概念的作者Robert C. Martin(他写了软件体系结构中最重要的书之一,即Clean Code)谈到“一类应该只有一个改变的理由” ,因此他将责任定义为更改。 例如,让我们考虑一个编译并打印报告的模块。 想象一下,可以出于两个原因而更改这种模块。 首先,报告的内容可能会改变。 其次,报告的格式可能会改变。 这两件事因不同的原因而改变。 一种实质性和一种化妆品。 单一责任原则说,问题的这两个方面实际上是两个单独的责任,因此应该放在单独的类或模块中。 耦合由于不同原因在不同时间发生变化的两件事,将是一个糟糕的设计。 使班级专注于单个关注点很重要的原因是,它使班级更加强大。 继续前面的示例,如果更改了报表编辑过程,则存在更大的危险,即如果打印代码属于同一类,则该打印代码将被破坏。 (示例摘自 Wikipedia ) 如果我们定义班级,知道他们在项目中的职责是什么,我们可以: 轻松了解其在代码各部分中的功能。 更快,更详细地修改现有逻辑。 以较少的问题查找错误或有害行为的来源。 不同类或模块中的抽象逻辑。 拆分时没有重大问题的实现,因此以后可以完全替换它们。 通过类或模块以更有效的方式定义单元测试,因此我们可以测试一小段代码,而无需测试更多的代码。 正如我之前说过的,您可以在类中考虑可扩展性以定义职责。 这就像思考是否可能在我们的项目中修改需求并在我们的体系结构中查看如何进行这些修改一样简单。 例如,如果我们看到要进行小的视图更改,则必须修改或修改业务逻辑,则我们没有正确定义项目中的职责。 让我们看一下Swift中的一个具体示例。 假设我有一个应用程序,它显示了商店中的商品列表。 到目前为止,我只有一个ItemsViewController负责该流的所有逻辑,数据和表示。 同样,当用户选择一个项目时,它也会打印日志。 您可以在https://github.com/fedejordan/SRPExample中查看代码 为此, ItemsViewController使用UITableView在列表中显示项目。 另外,我们使用一个名为ItemTableViewCell的UITableViewCell子类来显示这些元素。 但是,假设我们要更改视图,例如,使用UICollectionView. 在这种情况下会有什么问题? 视图代码与项目数据逻辑非常耦合。 如果我们更改视图,则很有可能也会修改负责该项目的类。 真正的问题在于以下几行: 让item […]

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 )将仅使用(并将暴露给它)所需的东西: […]

固体-iOSразработке。 Принципединственнойответственности

Введение ВэтомциклестателбырассказатьоSOLIDпринципах,и,чтосамоеглавное,дополнитихпра Понимаясмыслиспользованиякаждогопринципа,从 Немногоистории Вначале2000-хРобертМартин,такжеизвестныйкакдядюшкаБоб,придумалсписокиз11принриоооое Первые5принциповописывали,каксделатьхорошийдизайнкласса。 使用SOLID ,则必须使用ПозжеонисталиизвестныподговорящейаббревиатуройМайкломФизерсом。 Этипринципыпомогаютписатьнадежный,гибкийкод。 Надежностьозначаетпростотуипонятностькода ,чтопозволяетлегковноситьвнегоизменения,упро Гибкостьпозволяетсминимальнымиусилиямимасштабироватьпроект ,则需将кодовуюбазубезвредвредвред。 固体 Чтожеобозначаетаббревиатура? S — 单一责任原则(SRP ) (SRP )或Принципединственнойответственности O — OCP ( 开放式封闭原则 ) L —ПринципподстановкиБ。 Лисков(里斯科夫替代原理LSP ) I — ISP ( 接口隔离原则,Interface Separation Principle,ISP ) D — 依赖倒置原理,DIP )( D) (Принципинверсиизависимостей) Опытномуразработчикумогутбытьочевиднывсепринципыдажепослебыстроговзгляданаихрасшифров。 Нооченьчастомногиеначинающиеразработчикинедоконцапонимают,почемуэтипринципытакважны。 Дляустраненияэтогопробелавзнанияхдавайтепройдемсяпокаждомуизних。 Принципединственнойответственности РобертМартинописывалеготак: Каждыйклассдолжениметьтолькооднупричинудляизменения Другимисловами,объектдолженнеститолькооднуответственность。 Егоинтерфейсиимплементациядолжныбытьнаправленытольконаэтуответственность。 Принципдолженсоблюдатьсявсигда:нетолькоприпроектированиикласса,ноиприегобагфиксе。 Оченьчастокласссоздается,следуяпринципу,нопотомвнегопопадаетвсё,чтотольковозможно。 […]

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部分- 接口隔离原理。

固体-iOSразработке。 Принципоткрытостизакрытости

Ктобымогподумать,чтодвапротивоположныхпосмыслусловаокажутсярядом,образоваводинизосновныхпринциповпроектированияобъектно-ориентированногодизайна。 Напервыйвзглядтакоесловосочетаниеможетпоказатьсябессмыслицей。 Давайтевзглянемнаформулировку: Программныесущности(классы,модули,функцииит.п.)должныбытьоткрытыдлярасширеииннозак 1988年ИменновтакомвидепринциппоявилсявкнигеБертранаМейераеевдалеком。 ПозжеРобертМартинпопуляризовалего,включиввсвоюизвестнуюаббревиатуруSOLIDподбуквойО。 Пожалуй,единственноо,чтостановитсяяснымпослепрочтенияэтогоопределения,этотоечтот Ночтоимеетсяввиду-по-прежнемунепонятно。 Закрытость Начнемсзакрытостидляизменений。 Выкогда-нибудьзаходиливхедерыклассовот苹果,пытаясь(восновномслучайно)тамчто-тоизменить? Xcodeсразужесообщал,чутьлинецитируявышеупомянутуюформулировку,чтоданныйкларосзакрыт,ментель。 :тоиестьзакрытость:интерфейсыпривычнынам,потомучтоонинеизменны。 Мыпользуемсяимикаждыйдень,знаянаизустьназванияметодовисвойств。 Нопредставьте,еслибыназванияиливходныепараметрыуметодовменялисьскаждымобновлением。 Смоглибымыпомнитьих? Врядли,ноэтодаженесамоестрашное。 Чтобылобысмногочисленнымипроектамиповсемумиру,еслибывнезапноинженерыиз苹果решили,чтосвойство的backgroundColorкласса的UIViewчересчурдлинноеизаменилиегонаBGCOLOR,поставиввсехпередфактомсочереднымобновлением? Страшнопредставить,всколькихместахпридетсяменятькод,чтобыпростоскомпилироватьпроект。 Новсе-такиправокпроектыскомпилируются Такимиужаснымипоследствиямиможетобернутьсянарушениезакрытости。 Еслиперефразироватьэтучасть,классдолженоставатьсядоступнымдляиспользованиявдругихклассах。 Адляэтогоегопубличныйинтерфейс(поведение)надежнеевсегозафиксировать(закрытьотизменений)。 Открытость Чтожетеперь,зафиксироватьинтерфейсыинедобавлятьничегонового? Конечнонет,мыдолжнырасширятьклассы,делаяихболеефункциональными,имыможем, ведьоние 。 Возвращаяськэппловскимклассам,расширениемможноназватьвесьфункционал,околокотороговынайдетемакросNS_AVAILABLE(которыйобозначаетдоступностьсопределеннойверсииОС)。 Приэтомповедениестарыхметяднеменяется,какисамогоклассавцелом。 Такжерасширениеможетосуществлятьсяспомощьюнаследования:уклассов的UIImageViewили的UIScrollViewнамногобольшевозможностей,чемупростого的UIView,ноприэтомбазовоеповедениенеизменилось,мыуверенывэтом,чтопозволяетбезтрудазаменитьнанихвсе的UIViewвпроекте。 (чтотакжеявляетсяпримеромпринципаподстановки,окоторомпоговоримпозже)。 И,наконец,расширениефункциональностипрекраснореализуетсяспомощьюпротокола:объявиввнеминтерфейс,можносоздаватьсколькоугодновсеболееиболеесовременныхреализаций,отвечающихэтомуинтерфейсу。 应用程序:数据存储 删除:您需要先删除хранитьданныепользователя。 Выполнимеессамогоначала。 数据仓库(DataStore),数据仓库(отвечающийзалокальноехранение)中的Выделивответственностьизконтроллера。 Насталовремяреализоватьсохранениемоделипользователя,和самыйпростойспособ—照片作者 Вывод Принципоткрытостизакрытостипозволяетстроитьсистемутакимобразом,чтобыеёлегкобыломасштабироватьвбудущем,незатрагиваяприэтомфункциональностьужесуществующихчастей。 Приэтомнетнеобходимостипытатьсяпредусматриватьвсёзаранее。 Можносказать,чтоэтонекаярекомендацияобизоляциикомпонентовмодулей, Втожевремя,принципнестоитвосприниматькритично,создаваяабстракциивезде和гдетольковозм。 Наиболееправильнаярекомендация:создаватьабстракции,

Swift的SOLID面向对象设计原则

iOS首席工程师Buymie的Mnats Karakhanyan 好的软件系统应该具有好的架构和构建块。 如果没有很好地构建模块,那么体系结构就没什么大不了的,那么您最终可能会使用构建良好的模块构建一个糟糕的系统。 这就是SOLID原则的用武之地。 SOLID原理告诉我们如何将我们的功能和数据结构安排到类中,以及如何将这些类互连。 注意: “类”一词并不意味着这些原则仅适用于面向对象的软件。 类只是功能和数据的耦合分组。 每个软件系统都有这样的分组。 SOLID是5条原则的首字母缩写,代表: S:单一责任原则(SRP) O:开闭原理(OCP) L:李斯科夫替代原理(LSP) 一:接口隔离原理(ISP) D:依赖反转原理(DIP) 这些原则的目标是创建以下软件结构: 容忍变化 容易理解,并且 是可在许多软件系统中使用的组件的基础。 SRP:单一责任原则 类或模块应该有一个更改理由,并且只有一个更改理由 该原则为我们提供了责任的定义和班级规模的准则。 班级应该承担一项责任-改变的原因之一。 违反SRP的示例。 让我们看看这堂课有什么样的责任? 从API检索数据 解析API响应 将数据保存在数据库中 我们可以解决此问题,将职责移至类: 注意:听到这个名称太容易了,然后假设它意味着每个模块都只能做一件事 。 有这样的原则。 一个函数应该做一件事情,并且只能做一件事情。 违反原则的症状是意外复制和合并。 OCP:开放封闭原则 软件实体(类,模块,功能等)应打开以进行扩展,但应关闭以进行修改 让我们看一个违反OCP的例子。 考虑类AnaliticServise,该类正在跟踪Facebook中的事件。 现在,我们还想增加在Google Analytics(分析)中跟踪相同事件的可能性。 不违反OCP的解决方案可能如下所示: 注意:遵循OCP通常会引入新的抽象级别,这会增加我们代码的复杂性。 您想专注于最有可能在您的设计中发生变化的领域,并在其中应用原理。 LSP:李斯科夫替代原则 令Φ(x)是关于类型T的对象x的可证明性质。那么对于类型S的对象y,其中S是T的子类型,则Φ(y)应该成立。