Tag: 软件设计

架构A / B实验(iOS)

对产品进行实验是公司获得其客户最佳响应的最强大的技术之一。 但是有时候,尤其是当它们同时超过2个或3个时,对于在整个项目中找到实验块的开发人员来说,这可能会有些沮丧。 在本文中,我将解释一种简单的体系结构方法来保持我们的代码干净和动态。 什么是A / B测试? 假设我们要改善应用程序的功能,以吸引更多用户使用它。 我们几乎没有改善它的想法(涉及设计,可见性等),但是我们不确切知道哪一个最能使用户获得最大响应。 解决方案是设置A / B测试 。 A / B测试是根据特定版本随机分配用户的实验。 让我们看看它是如何工作的: 假设我们想知道哪个是最好的价格,我们应该出售我们的高级会员才能获得最高的收入。 我们决定设置3种变化: 原始价格变化 :15,00 $; 变化A :25,00 $; 方案B :40,00 $。 通过特定API(即Apptimize API)使用我们应用程序的每个用户都将被分配为这些变体之一,并且该服务将向我们的客户返回已分配用户的变体。 此时,根据选择的变体,我们将执行一段代码而不是另一段代码。 在上面的示例中,用户将看到为其分配的变体选择的价格。请注意, 实验必须始终包含原始变体 ,换句话说,就是开始实验之前存在的变体,否则我们永远不会知道这是否是最好的解决方案,或者实际上是否还不够好。 做得好,实验正在运行! 现在,我们只需要等待任意时间即可获得结果。 好的,已经过去了一周,我们发现了以下结果: 原始变体: 1000个用户支付了15,00 $-> 15.000,00 $的收入 ; 变体A: 800个用户支付了25,00 $-> 20.000,00 $的收入 ; 变体B: 300个用户支付了40,00美元-> 12.000,00美元的收入 ; 请注意,最低价或最高价均未赢得! 实际上, 变体A […]

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上找到 。 这是其中一种更为自我描述的设计模式。 责任链设计模式允许一连串不同的对象(从相同的类/接口继承),将执行一段逻辑的职责传递给另一个对象。 在此示例中,我们将使用一名需要支付费用的员工,而该费用需要他们结清。 在我们的示例业务中,他们有几种选择可以清除这笔费用。 雇员 员工可以清除0至100英镑之间的所有费用。 老板 如果员工的支出大于此 ,则可以与老板联系,后者有能力结清101-1000英镑之间的费用。 首席执行官 除此之外,员工还需要与公司的首席执行官联系,后者对任何费用都严格限制了10,000英镑的费用。 其他任何事情都可能不会得到公司的批准。 如您所见,这形成了自然的“链”: 在我们的代码中,CEO,老板和员工是同一链的一部分,因此它们将实现相同的接口。 如果他们不能批准费用,他们将有能力将责任推上链。 用于此的UML如下所示: 码 首先,在Xcode中创建一个新项目。 我们不会在UIKit中碰任何东西,因此创建一个macOS Terminal项目。 首先创建一个Expenditure类。 这是一个简单的类,将仅容纳我们的费用。 在现实生活中,这可能包含更多相关信息。 代码如下所示: 然后,我们将创建Chain协议。 该协议将能够沿着管理“链”传递责任。 它看起来像这样: 员工,老板和首席执行官都将实施此协议。 如果费用金额太大而他们无法批准,他们将把费用沿着链条传递。 CEO代码与其余代码略有不同。 您可能还记得,CEO只能接受少于10,000英镑的同意。 为了停止销售,我们检查金额是否超过10,000英镑,并进行打印以告知用户: 然后,在我们的主类中,我们将其链接在一起。 首先,我们需要这样创建员工,老板和CEO对象: 然后,我们在每个适用位置上调用setNextManagementLevel() 。 它看起来像这样: 然后,我们与员工展开连锁反应: 现在,您完整的main.swift文件将如下所示: 运行您的程序,您将在终端中看到以下输出: 员工可以批准这笔支出 你的老板可以批准这笔支出 首席执行官可以批准这笔支出 这项支出过大,不会获得批准 程序以退出代码结束:0 欢呼! 您已经实现了管理层次结构-我的意思是-责任链设计模式。 您可以在Git上找到所有与此相关的代码。 山姆·斯通(@ […]

我让我的iOS项目陷入混乱。

不久前,我休了几个星期的假期。 作为首席开发人员,我对团队充满信心,并知道所有团队成员都是经验丰富的开发人员,并且对项目本身有丰富的知识。 他们中的大多数人比我拥有更多的语言经验,并且所有人都在使用TDD进行工作。 该项目掌握得很好。 欢迎回来! 我同样充满信心地回来了,很高兴看到项目经理和产品负责人对团队所做的工作感到满意。 团队很高兴地告诉我,一切进展顺利,他们提供了所有预期的功能,甚至开始从事一些期待已久的技术任务。 愿景很简单: 文件无处不在 。 我打开了这个项目,意识到了以前错过的一切,为什么我的头脑不明白发生了什么。 该项目的组织无处不在。 名称难以理解的文件夹,来自项目层次结构中同一级别上不同层的对象文件。 对于这个项目的新手来说,根本不可能理解该看哪里。 我开始了解发生了什么,但还不确定为什么会发生。 直到我问了一个关于两个特定类的角色的问题,这两个类应该是同一类的,得到的答案是: 好吧,是的,该类以这种方式工作,但是另一类则根本不以这种方式工作。 我相信,即使您有时在性能上有所损失,但优势仍然太大,以至于您无法在域元素周围阐明模块。 人脑只能弥补很多东西,而当功能实际上被分为多个模块时,很容易就看不到您实际在做什么。 您的项目应始终反映您所做的设计决策,并且应明确您的工作内容。 如果您喜欢这篇文章并认为其他人可能会感兴趣,请随时分享并给予一些鼓掌。 我计划写更多文章,所以 如果您想了解下一个内容,请在这里 关注我 。 您也可以在 Twitter 上 关注 我,讨论任何事情。

测试驱动开发:开发人员魔术棒

让我们讲一个故事。 “不久以前,我已经为即将上线的测试仪提供了我的应用程序的构建。 我已经对其进行了一些修复,根据我的开发人员健全性测试,这些构建已使生产准备就绪。 但是经过测试,情况完全不同。 在某些情况下,由于某些较早的修复程序,我遇到了错误。 搞什么鬼??? 除了使我的构建稳定之外,我使它更加不稳定。” 我不是唯一遇到这种情况的人,但是许多开发人员也遇到了这种情况。 那么我们该怎么做呢? 答案是测试驱动开发(TDD)。 我们要学什么? TDD上有很多博客。 我将列出您可以在其中找到的一些最佳参考。 我们不会讨论理论上的TDD概念,而是将重点放在我们如何从开发人员的角度计划实现TDD。 什么是TDD? 为什么选择TDD? 如何规划TDD? TDD的警告。 您需要TDD吗? 看起来太多了吗? 不用担心,我们会做的很快而简短。 😉 什么是TDD? 测试驱动开发是美国软件工程师肯特·贝克(Kent Beck)提出的开发程序,在编写代码的同时,我们还记录了测试用例。 从而允许我们在继续开发的同时测试我们的代码。 下图描述了传统开发与TDD之间的区别。 在传统开发中,我们首先开发代码,完成功能并进行手动测试。 但是在TDD中,我们首先写下测试用例,然后相应地开发代码。 这有助于我们最大程度地减少代码失败或错误的机会。 如果不更新旧的测试用例,则开发的任何新功能也应尊重现有的测试用例。 TDD基于RGR的概念,即红色,绿色和重构。 红色:我们首先编写失败的测试用例。 绿色:我们编写通过测试所需的最少代码。 重构:如果需要测试代码,我们也会重构代码。 为什么选择TDD? 最大限度地减少代码失败的机会。 最小化团队中任何新开发人员在代码库上工作的机会。 由于引入了新功能或错误修复,破坏现有功能的机会较小。 提高产品知识。 改进编码标准。 如何规划TDD? 百万美元问题来了? 我们都知道什么是TDD。 但是我们如何计划TDD? 我们将如何决定需要编写哪些测试用例? 我要编写并开发该功能,然后为它们编写测试还是将来如何? 困惑? 了解正确实施的功能。 让我们以以下要求为例: 要求: 构建一个程序,该程序将从用户那里获取城市的人口输入,并返回该城市所属的类别。 以下是城市类型及其人口范围: 小(5,000至10,000) 中(10,000至50,000) […]

Swift中的设计模式:命令模式

欢迎来到一系列致力于学习设计模式的文章。 尽管许多想法与代码无关,但我们的目标是向您展示如何在Swift中实现它们(在撰写本文时为Swift 3.0)。 每个帖子彼此独立,所有项目代码都可以 在Git上找到 。 好的,在今天的示例中,为您的iPhone拍照,特别是在iPhone上的跳板。 您拥有一个可供用户随时点击并执行的应用程序网格。 您还有一个主页按钮可以关闭您的应用程序(好的,没有,它会将它们推送到后台,但请与我们在一起)。 每个应用程序在运行和关闭时都会运行自己的特定命令列表。 此刻,当您-iPhone-查看“天气和时钟”应用程序时,您会看到以下功能: 您如何启动应用程序? 好吧,我想您会先在WeatherApp上调用getWeatherData() ,然后再调用showWeatherAnimation()。 在ClockApp上,您只需要调用showClock()。 对于可怜的iPhone有点困惑。 两个完全不同的对象,如果没有对对象如何工作的事先了解,就无法真正知道该怎么做。 iPhone需要做的是将这些命令封装到另一个功能中,以便它知道如何启动该应用程序,而不必掌握有关该应用程序如何工作的复杂知识。 这可以通过命令模式解决。 (此示例确实是一个延伸。iPhone将使用更复杂的系统,这是现实生活,但是它提供了一个很好的视觉概念,我们可以用脑海中的所有图片来演示图案的工作原理!) Command模式允许我们做的是将Commands转换为对象,这些对象将根据对象执行正确的功能列表,而无需修改对象本身(如果不得不询问这些对象的开发者,将是一件痛苦的事情。两个应用程序以更改其代码库)。 例如,我们将创建一个名为WeatherAppOpen的类,该类符合Command协议,并在调用通用execute()函数时执行getWeatherData()和showWeatherAnimation() 。 这就是iPhone跳板的Command模式的UML形式: Springboard从不与WeatherApp直接通信,因为必要的命令被抽象为通用的通用方法,这意味着Springboard只需要知道一种方法即可。 它在代码中的外观 首先,在Xcode中创建一个新项目。 我们不会在UIKit中碰任何东西,因此创建一个macOS Terminal项目。 我们将首先创建所有命令类都将遵循的Command协议。 它相对简单,仅包含Springboard将与之交谈的execute()函数。 接下来,我们将创建WeatherApp和ClockApp类。 这些类仅包含一些输出到控制台的样板方法,因此我们可以看到理论上的Springboard将会做什么。 它们将如下所示: 现在,我们需要通过Command协议访问这些方法。 为此,我们将对象创建为希望完成IE打开或关闭应用程序的Command类型。 从打开Weather应用程序开始,我们将创建WeatherAppOpenCommand 。 我们通过传递Weather App的实例来调用该类(如果这是现实生活,那么在iPhone中可能是Singleton,即将发布!)。 调用execute()时 ,我们随后使用对Weather App的特定命令启动该应用程序。 我们通过关闭Weather App进行相同的操作,如下所示: 开始拍照了吗? 然后,我们可以以完全相同的方式构建Clock App命令,但是调用特定于Clock App的方法: 现在,我们可以看看构建我们的假装(非常简单)的跳板。 Springboard需要能够容纳一系列打开和关闭命令(我们的应用程序)。 然后,如果有可用的应用程序,则需要能够在特定索引处执行命令。 它看起来像这样: setCommand函数使我们能够有效地将应用程序放置在Springboard的索引处,而open / […]