Tag: 软件设计模式

Swift中的设计模式:观察者模式

欢迎来到一系列致力于学习设计模式的文章。 尽管许多想法与代码无关,但我们的目标是向您展示如何在Swift中实现它们(在撰写本文时为Swift 3.0)。 每个帖子彼此独立,所有项目代码都可以 在Git上找到 。 观察者模式允许对象订阅所谓的主题。 主题更新后,将通知所有已订阅主题的对象有关更新的信息。 当对象之间存在一对多关系时使用。 观察者模式的常见实现使您能够: 添加一个观察者 这实际上告诉主题“嘿,我想加入循环”,并且在更新其他观察者时保持更新。 将该主题想成小组中总是有八卦并且想告诉别人的人。 通知观察员 观察者订阅后,我们将调用接口中定义的共享函数。 所有观察者都将实现此接口。 移除观察者 可能希望删除对象,而不再希望对其进行更新,因此我们必须给予机会删除它们。 许多语言和SDK都有自己的实现。 iOS将其与Notification Center及其NSNotificationCenter类一起使用。 使用“推送通知”,用户可以订阅以收听来自应用程序的更新,然后应用程序/服务器( 主题 )将通知推送到用户设备,并且用户始终可以选择通过删除应用程序来取消订阅。 我们将使用一个用户希望以二进制,八进制和十六进制格式显示数字的示例。 他们只想输入一次该号码。 该程序的UML图将如下所示: 首先,在Xcode中创建一个新项目。 我们不会在UIKit中碰任何东西,因此创建一个macOS Terminal项目。 首先,我们将从创建观察者协议开始。 该协议将包含id属性。 这将使我们以后可以删除观察者。 该代码将如下所示: 接下来,我们将创建Subject类。 这将是负责通知对象有关更新的类。 当我们附加一个Observer时,我们将其添加到ObserverArray中 。 当我们通知其他对象时,我们遍历ObserverArray并调用Observer协议中包含的update()函数。 看起来像这样: 继承Observer协议的类的工作是为Subject分配一个id,并在从Subject调用update()方法时执行该方法。 它们将如下所示: 现在是时候将它们放在一起了。 在主类中,我们要创建一个Subject对象。 我们创建的所有观察者将被分配相同的Subject 。 然后,我们给主题分配一个数字。 然后,我们将再次进行显示更新。 它看起来像这样: 终端输出将如下所示: 二进制:1111 八进制:17 十六进制:f 二进制数:10 八进制:2 […]

Swift中的设计模式:策略模式

欢迎来到一系列致力于学习设计模式的文章。 尽管许多想法与代码无关,但我们的目标是向您展示如何在Swift中实现它们(在撰写本文时为Swift 3.0)。 每个帖子彼此独立,所有项目代码都可以 在Git上找到 。 策略模式允许您在运行时更改算法的行为。 使用接口,我们能够定义一系列算法,封装每个算法,并使它们可互换,从而允许我们选择在运行时执行哪种算法。 一个例子就是游戏中的角色。 我们的角色-好人-有3种状态:奔跑,步行和站立。 我们希望将状态与其他对象(即Bad Guy角色)一起重用,因此我们希望封装它们,并能够将状态与其他对象一起使用,从而减少了代码量。 我们可以使用UML图显示Good Guy角色如何工作: 首先,在Xcode中创建一个新项目。 我们不会在UIKit中碰任何东西,因此创建一个macOS Terminal项目。 首先,我们需要定义接口/协议(这是同一件事,但是Swift使用单词protocol)。 我们的基本接口定义了一种称为enterState()的方法。 它看起来像这样: 我们可以建立Good Guy类。 此类被设置为接收我们的状态并在我们传递给它的类中执行enterState()方法。 它看起来像这样: 然后我们可以建立我们的状态类。 我们将创建步行,跑步和站立状态。 这个想法是,好家伙将使用这些特征,但坏家伙也将能够在实现时通过实现CharacterState接口来使用它们。 它们非常基础,仅运行打印命令来表明我们已进入该状态。 这是我们各州的实现: 现在,在您的主类中,我们希望能够使用内置在其中的setState函数将状态传递给您的好家伙。 它看起来像这样: 我们将不同的状态传递给我们的Good Guy类,它将执行每个不同的(尽管是基本的)算法。 点击运行,您的输出将如下所示: 进入步行状态 进入站立状态 进入运行状态 程序以退出代码结束:0 欢呼! 您已经实现了策略模式。 奖励-坏家伙 因此,如上所述,“坏人”还可以利用“好人”的功能,而不必重复任何代码(“策略”模式的一大好处)。 创建一个名为BadGuy.swift的新类。 该类看起来与Good Guy类似,但您可以向其中添加特定的Bad Guy功能(例如杀死或射击)。 坏家伙看起来像这样: 为了说明它是如何工作的,在我们的主类中,我们将创建一个Bad Guy对象,并设置状态,就像我们对Good Guy所做的一样,因此现在看起来像这样: 运行它,您将得到以下结果: 进入步行状态 进入站立状态 进入运行状态 进入步行状态 […]

MVC分解原理

iOS社区一直在寻找一种统一的架构来统治一切。 不幸的是没有。 为什么我要写这个。 我在专业项目上使用了MVC , MVVM , MVP , VIPER等各种架构。 然后,我在寻找再次创建健壮的应用程序的正确方法。 有一天,我看到了由Guilherme Rambo创建的这个很酷的网站。 然后我开始思考,也许问题不在于架构。 也许我们的观点是问题所在。 因此,对于下一个项目,我决定选择MVC 。 在这篇文章中,您将了解这种方法。 问题 有关WMVC(错误的MVC)的主要问题是职责分配不平衡。 控制器几乎可以执行所有操作。 联网 资料解析 商业逻辑 动画制作 布置视图 用数据更新视图 响应用户输入 导航 因此,它很难测试,难以维护,难以修复。 UIViewController不是屏幕。 这是一个组成部分。 解 您可以在一个屏幕中使用许多视图控制器。 每个视图控制器都应承担自己的责任。 假设您从服务器加载数据并在屏幕上显示加载状态,然后响应出现,如果您的网络请求失败(总是失败),则在屏幕上显示数据状态或错误状态。 所有这些过程可能由一个视图控制器负责。 我们可以称之为StateViewController。 该视图控制器仅代表三种可能的状态。 您可以阅读John Sundell的这篇文章,以了解如何实现状态视图控制器。 当您需要针对不同屏幕的某些功能时,它非常简单,可以节省大量时间。 例 让我们来看一个例子。 我想创建一个Netflix细节场景。 我将场景分为如下所示的较小部分。 当然这是一个演示,我只想展示如何在屏幕上分解为较小的组件。 DetailViewController应该能够由其他视图控制器组成。 像HeaderViewController , DescriptionViewController , ActionsViewController等一样。这将提供容易的错误修复, 毫不费力地添加新功能,并且为大型控制器编写单元测试比大型视图控制器容易得多。 DismissViewController的责任是显示关闭按钮和火灾关闭事件。 […]

iOS设计模式

适配器,责任链,装饰器,门面 在本文中,我将介绍四种设计模式,您可以将它们与当前项目相关联,并且可以通过示例(例如可可如何适应这些模式)轻松理解。 让我们从适配器开始。 适配器设计模式将类的接口转换为客户端期望的另一个接口。 Adapter使类可以协同工作,否则由于接口不兼容而无法实现。 它将客户端与目标对象的类分离。 可可如何适应适配器设计模式? 通讯协定 协议是一种语言级别的(Objective-C)功能,可以定义作为适配器模式实例的接口。 协议本质上是一系列与类无关的方法声明。 (在Java中, 接口与协议同义。)如果希望客户端对象与另一个对象进行通信,但是对象的不兼容接口使此操作变得困难,则可以定义协议。 然后,另一个对象的类正式采用该协议,并通过实现该协议的一种或多种方法来“符合”该协议。 该协议可能需要一致的类来实现其某些方法,而其他类的实现则可选。 然后,客户端对象可以通过协议接口将消息发送到另一个对象。 注意:协议的设计与适配器模式的描述不完全匹配。 但这达到了模式的目标:允许具有其他不兼容接口的类一起工作。 用途和局限性 例如,Foundation框架包括NSObject , NSCopying和NSCoding协议,它们都是非常重要的协议。 AppKit协议包括NSDraggingInfo , NSTextInput和NSChangeSpelling 。 UIKit协议包括UITextInputTraits , UIWebViewDelegate和UITableViewDataSource 。 责任链设计模式通过为多个对象提供处理请求的机会,将请求的发送者与接收者分离。 模式将接收的对象链接在一起,并沿着链传递请求,直到对象处理它为止。 链中的每个对象要么处理请求,要么将请求传递给链中的下一个对象。 可可如何适应责任链设计模式? 响应链 应用程序框架包括称为响应者链的体系结构。 该链由一系列响应程序对象(即,从NSResponder继承的对象,或者在UIKit中,从UIResponder继承的UIResponder )组成,事件(例如,鼠标单击)或操作消息将沿着该UIResponder传递并(通常)最终得到处理。 如果给定的响应者对象不处理特定消息,则它将消息传递给链中的下一个响应者。 响应者对象在链中的顺序通常由视图层次结构决定,层次结构中从低层响应者到高级响应者的发展,最终以管理视图层次结构的窗口对象,窗口对象的委托,或全局应用程序对象。 响应者链上事件和操作消息的路径是不同的。 一个应用程序可以具有与窗口(甚至视图的局部层次结构)一样多的响应者链。 但是一次只能有一个响应者链处于活动状态,即与当前活动窗口关联的响应链。 注意:与响应者链密切相关的视图层次结构的设计适应了Composite模式。 用途和局限性 通过使用Interface Builder或以编程方式为程序构造用户界面时,您可以“免费”获得一个或多个响应者链。响应者链与视图层次结构紧密相关,在您创建视图时会自动获得它。对象是窗口内容视图的子视图。 如果您有一个自定义视图添加到视图层次结构,它将成为响应者链的一部分。 如果实现适当的NSResponder或UIResponder方法,则可以接收和处理事件和操作消息。 作为窗口对象或全局应用程序对象(AppKit中的NSApp )的委托的自定义对象也可以接收和处理这些消息。 装饰器设计模式动态地将附加职责附加到对象上。 装饰器为子类提供了灵活的替代方案,以扩展功能。 与子类一样,修饰器模式的改编允许您合并新行为而无需修改现有代码。 装饰器包装其行为扩展的类的对象。 它们实现与包装对象相同的接口,并在将任务委派给包装对象之前或之后添加自己的行为。 […]

开闭原则:正确扩展实体

在SOLID首字母缩略词“封闭式原理”中包含的所有设计原理中,可能是最难理解且最经常破解的。 毕竟,如果您想向实体中添加功能,为什么不直接将其添加到实体的现有实现中呢? 作为新手程序员,我肯定会感到这种感觉。 本文的目的是首先讨论“开放式封闭原则”背后的原理,然后研究三种正确扩展实体的技术。 什么是开放式封闭原则,为何如此重要? 让我们从Bertrand Meyer最初编写的定义开始: 软件实体(类,模块,功能等)应打开以进行扩展,但应关闭以进行修改 本质上,我们是在谈论以某种扩展方式向实体添加任何新属性或方法,而不是将它们直接放入实体中(然后进行修改)。 那是有道理的,但是为什么我们要在这个麻烦上走? 这是一些原因… 遏制错误的引入 假设我正在编写一个供第三方使用的软件包,并决定要向现有实体添加一些新功能。 从表面上看,这似乎很好。 但是,当我进行这些更改时,我并未考虑系统中其他实体与我刚刚修改的实体进行交互的所有不同方式。 我的修改会产生意想不到的影响,导致系统其他部分出现错误吗? 通常,我们希望逐步开发我们现有的代码,以使其尽可能高效且无错误。 在此过程中引入新的属性或方法可能会取消数月甚至数年的迭代改进。 单独关注 当我向现有实体介绍新功能时,很可能会打破SOLID原则中的不止一项。 如果新功能使我的实体超出其原始用途的范围,我还将打破“单一责任原则”。 为了代码可维护性,我应该保留原始实现,并使用其他方法对其进行扩展。 设计更好的实体 在采用开放式封闭原则的所有原因中,最重要的是它鼓励我们通过使用泛型和面向协议的编程(POP)原则来创建具有良好抽象平衡的实体。 试想一下,如果我知道我不能回到自己的实体来更改类型,那么我将非常谨慎地以能够提供最大灵活性的方式进行设计。 结果是我们最终可以编写更少的代码,并使我们编写的代码适用于更多情况。 简而言之,我们编码更好。 显然,这里可能会有更多的讨论,但是为了继续学习一些有用的技术,让我们考虑最后一个大问题…… 我应该严格遵循开放式原则吗? 遵守开放式封闭原则是一个主观选择,开发人员通常在何时扩展实体上有不同的看法。 毕竟,您的实体可能被认为是“完成”或“进行中的工作”,最终您作为开发者是做出这种区分的仲裁者。 以我的经验,如果您决定实体需要您想要实现的功能来履行其单一职责,那么请继续添加它们(无罪感!)。 否则,这里有一些有关如何正确扩展它们的想法。 扩展技术 在下一部分中,我们将介绍三种扩展技术并对其进行批判。 事不宜迟,让我们熟悉在以下示例中将要使用的惊人实体: 是的,这真是好人。 技术#1:协议扩展(OK) 假设我希望我的“ Thingy”能够摆动(不要问我为什么……)。 我的第一个直觉可能是这样做: 显然,这违反了我们的开放式封闭原则。 因此,一种方法可能是创建一个声明该方法的协议,然后将其实现放入我们的struct的扩展中: 很好。 请注意,方法的声明发生在协议中(第9行),其实现发生在实体的扩展中(第15行)。 我们可能考虑的一种变体是将方法的实现放入协议扩展中。 这将使我们能够在符合协议的任何实体上使用相同的方法实现。 看起来像这样: 这里要注意的最大区别是,我们的摆动方法的声明和实现现在都在协议的扩展中(第9-15行)。 当实体符合协议时(第17行),现在无需实现该方法。 因此,通过第一组示例,我们从技术上扩展了我们的实体,而没有对其进行修改,但是请注意! 首先,这种技术仅适用于方法,不适用于属性,因为我们无法在扩展中存储任何内容。 其次,也许还有一个更大(但不太明显)的缺点是,我们不再能够像添加摆动方法之前那样使用“ Thingy”的原始实现。 技术#2:类继承(OK) […]

Swift中的设计模式:装饰器模式

欢迎来到一系列致力于学习设计模式的文章。 尽管许多想法与代码无关,但我们的目标是向您展示如何在Swift中实现它们(在撰写本文时为Swift 3.0)。 每个帖子彼此独立,所有项目代码都可以 在Git上找到 。 想象一下,您在苹果公司工作,而您要负责设计苹果网站的后端。 我们特别感兴趣的是该站点的MacBook订购功能。 尽管只有3款MacBook(在撰写本文时为MacBook,Air和Pro),但MacBook的组合数量会大大增加。 从软件设计的角度来看,用于订购过程的最明显的OOP设计风格可能是继承(多态)。 您将从基类(例如MacBook)开始,它将包含与每个MacBook相关的各种属性(即成本和说明)。 一对一的示例如下所示: 那很棒。 但是考虑一下苹果网站上实际有多少种MacBook组合? 我还没有完成数学运算,但是它必须在100年代。 按照这条路线,您将得到一个看起来像这样的设计: 它开始显得凌乱。 对于每个模型,您需要添加一个新的子类。 如果价格变动怎么办? 每个受价格变化影响的模型都需要更新,而这将变成一大堆麻烦。 装饰器模式在这里成为自己的特色。 它允许我们在运行时向对象添加其他要求。 我们可以使用用户在订购时选择的所有其他功能来“装饰”我们的基本MacBook类。 它遵循“开放-封闭”原则,即是的,对象应该是可扩展的,但不能,您不能更改现有代码(以CocoaPods为例)。 Decorator模式确实使用继承,但是以一种自然的,更有限的方式。 这样可以确保将来扩展系统不会像上图那样结束。 您仅需使用少数子类,就可以进行MacBook所需的100多种定制。 看起来像什么 装饰器模式包含一些基本组件,我们在运行时对其进行了扩展,并用装饰器类进行“装饰”。 在MacBooks的情况下,核心组件将是MacBook(在本示例中,称为MacBook Regular,以免引起混淆!),MacBook Pro和MacBook Air。 这些都将遵循相同的协议,并且能够覆盖协议中的属性,从而赋予其独立性(即,每台MacBook的成本不同)。 装饰器将从装饰器类继承,该装饰器类符合基本协议。 装饰器类包含基础组件的实例,我们将其用于将核心组件包装在装饰器中。 这是一个大致的示意图: 当我们使用MacBook示例进行总结时,您开始了解装饰器模式的工作方式: 它在代码中的外观 首先,在Xcode中创建一个新项目。 我们不会在UIKit中碰任何东西,因此创建一个macOS Terminal项目。 这些类都不复杂。 让我们从所有对象与以下对象交互的基本MacBook类开始: 这只是说所有符合MacBook协议的对象都应包含返回正确值类型的成本和描述项。 所有的ConcreteComponents都符合它,如下所示: 它们扩展协议属性并返回自己的值。 接下来,我们看一下MacBook Decorator。 该类符合MacBook协议,这意味着我们可以返回传递给它的MacBook对象的成本和描述。 这将是所有装饰器都继承自的类: 我们的装饰器是从此类继承的,这意味着它们能够添加自己的单独成本,并添加到传递给它的MacBook实例的描述中。 它们看起来像这样: 完善。 现在,我们要做的是模拟MacBook的自定义。 […]