网易首页 > 网易号 > 正文 申请入驻

ZOMBIES:在软件开发中定义边界和接口(三) | Linux 中国

0
分享至

导读:丧尸是没有边界感的,需要为你的软件设定限制和期望。

本文字数:8328,阅读时长大约: 10分钟

丧尸是没有边界感的,需要为你的软件设定限制和期望。

丧尸没有边界感。它们踩倒栅栏,推倒围墙,进入不属于它们的地盘。在前面的文章中,我已经解释了为什么把所有编程问题当作一群丧尸一次性处理是错误的。

ZOMBIES 代表首字母缩写:

Z– 最简场景(Zero)

O– 单个元素场景(One)

M– 多个元素场景(Many or more complex)

B– 边界行为(Boundary behaviors)

I– 接口定义(Interface definition)

E– 处理特殊行为(Exercise exceptional behavior)

S– 简单场景用简单的解决方案(Simple scenarios, simple solutions)

在本系列的前面两篇文章中,我演示了 ZOMBIES 方法的前三部分:最简场景、单元素场景和多元素场景。第一篇文章 linux.cn,它提供了代码中的最简可行路径。第二篇文章中针对单元素场景和多元素场景 linux.cn。在这篇文章中,我将带你了解边界和接口。

回到单元素场景

要想处理边界,你需要绕回来(迭代)。

首先思考下面的问题:电子商务的边界是什么?我需要限制购物框的大小吗?(事实上,我不认为这有任何意义。)

目前唯一合理的边界条件是确保购物框里的商品数量不能为负数。将这个限制表示成可运行的期望:

  1. [Fact]

  2. public void Add1ItemRemoveItemRemoveAgainHas0Items() {

  3. var expectedNoOfItems = 0;

  4. var actualNoOfItems = -1;

  5. Assert.Equal(expectedNoOfItems, actualNoOfItems);

  6. }

这就是说,如果你向购物框里添加一件商品,然后将这个商品移除两次,shoppingAPI的实例应该告诉你购物框里有零个商品。

当然这个可运行期望(微测试)不出意料地会失败。想要这个微测试能够通过,最小改动是什么呢?

  1. [Fact]

  2. public void Add1ItemRemoveItemRemoveAgainHas0Items() {

  3. var expectedNoOfItems = 0;

  4. Hashtable item = new Hashtable();

  5. shoppingAPI.AddItem(item);

  6. shoppingAPI.RemoveItem(item);

  7. var actualNoOfItems = shoppingAPI.RemoveItem(item);

  8. Assert.Equal(expectedNoOfItems, actualNoOfItems);

  9. }

这个期望测试依赖于RemoveItem(item)功能。目前的shippingAPI还不具备该功能,你需要增加该功能。

回到app文件夹,打开IShippingAPI.cs文件,新增以下声明:

  1. int RemoveItem(Hashtable item);

ShippingAPI.cs中实现该功能:

  1. public int RemoveItem(Hashtable item) {

  2. basket.RemoveAt(basket.IndexOf(item));

  3. return basket.Count;

  4. }

运行,然后你会得到如下错误:

Error

系统在移除一个不在购物框的商品,这导致了系统崩溃。加一点点 防御式编程defensive programming:

  1. public int RemoveItem(Hashtable item) {

  2. if(basket.IndexOf(item) >= 0) {

  3. basket.RemoveAt(basket.IndexOf(item));

  4. }

  5. return basket.Count;

  6. }

在移除商品之前先检查它是否在购物框中。(你可能试过用捕获异常的方式来处理,但是我认为上面的处理方式更具可读性。)

更多具体的期望

在讲更多具体的期望之前,让我们先探讨一下什么是接口。在软件工程中,接口表示一种规范,或者对能力的描述。从某种程度上来说,接口类似于菜谱。它罗列出了制作蛋糕的原材料,但它本身并不能吃。我们只是按照菜谱上的说明来烤蛋糕。

与此类似,我们首先通过说明这个服务能做什么的方式来定义我们的服务。这个描述说明就是所谓的接口。但是接口本身并不能向我们提供任何功能。它只是指导我们实现指定功能的蓝图而已。

到目前为止,我们已经实现了接口(只是某部分实现了,稍后还会增加新功能)和业务处理边界(也就是购物框里的商品不能是负数)。你指导了shoppingAPI怎么向购物框添加商品,并通过Add2ItemsBasketHas2Items测试验证了该功能的有效性。

然而仅仅具备向购物框添加商品的功能还不足以使其成为一个网购应用程序。它还需要能够计算购物框里的商品的总价。现在需要增加另一个期望。

按照惯例,从最直接明了的期望开始。当你向购物框里加入一件价值 ¥10 的商品时,你希望这个购物 API 能正确地计算出总价为 ¥10。

第五个测试(伪造版)如下:

  1. [Fact]

  2. public void Add1ItemPrice10GrandTotal10() {

  3. var expectedTotal = 10.00;

  4. var actualTotal = 0.00;

  5. Assert.Equal(expectedTotal, actualTotal);

  6. }

还是一样的老把戏,通过硬编码一个错误的值让Add1ItemPrice10GrandTotal10测试失败。当然前三个测试成功通过,但第四个新增的测试失败了:

  1. A total of 1 test files matched the specified pattern.

  2. [xUnit.net 00:00:00.57] tests.UnitTest1.Add1ItemPrice10GrandTotal10 [FAIL]

  3. X tests.UnitTest1.Add1ItemPrice10GrandTotal10 [4ms]

  4. Error Message:

  5. Assert.Equal() Failure

  6. Expected: 10

  7. Actual: 0

  8. Test Run Failed.

  9. Total tests: 4

  10. Passed: 3

  11. Failed: 1

  12. Total time: 1.0320 Seconds

将硬编码值换成实际的处理代码。首先,检查接口是否具备计算订单总价的功能。根本没有这种东西。目前为止接口中只声明了三个功能:

1.int NoOfItems();

2.int AddItem(Hashtable item);

3.int RemoveItem(Hashtable item);

它们都不具备计算总价的能力。所以需要声明一个新功能:

  1. double CalculateGrandTotal();

这个新功能应该让shoppingAPI具备计算总价的能力。这是通过遍历购物框中的商品并把它们的价格累加起来实现的。

修改第五个测试:

  1. [Fact]

  2. public void Add1ItemPrice10GrandTotal10() {

  3. var expectedGrandTotal = 10.00;

  4. Hashtable item = new Hashtable();

  5. item.Add("00000001", 10.00);

  6. shoppingAPI.AddItem(item);

  7. var actualGrandTotal = shoppingAPI.CalculateGrandTotal();

  8. Assert.Equal(expectedGrandTotal, actualGrandTotal);

  9. }

这个测试表明了这样的期望:如果向购物框里加入一件价格 ¥10 的商品,然后调用CalculateGrandTotal()方法,它会返回商品总价 ¥10。这是一个完全合理的期望,它完全符合商品总价计算的逻辑。

那么怎么实现这个功能呢?就像以前一样,先写一个假的实现。回到ShippingAPI类中,实现在接口中声明的CalculateGrandTotal()方法:

  1. public double CalculateGrandTotal() {

  2. return 0.00;

  3. }

现在先将返回值硬编码为0.00,只是为了检验这个测试能否正常运行,并确认它是能够失败的。事实上,它能够运行,并且如预期一样失败。接下来的工作就是正确实现计算商品总价的处理逻辑:

  1. public double CalculateGrandTotal() {

  2. double grandTotal = 0.00;

  3. foreach(var product in basket) {

  4. Hashtable item = product as Hashtable;

  5. foreach(var value in item.Values) {

  6. grandTotal += Double.Parse(value.ToString());

  7. }

  8. }

  9. return grandTotal;

  10. }

运行,五个测试全部通过!

从单元素场景到多元素场景

现在是时候进入下一轮迭代了。你已经通过处理最简场景、单元素场景和边界场景迭代地构建了系统,现在需要处理稍复杂的多元素场景了。

快捷提示:由于我们一直在针对单个元素场景、多元素场景和边界行为这三点上对软件进行迭代改进,一些读者可能会认为我们同样应该对接口进行改进。我们稍后就会发现,接口已经完全满足需要了,目前没有新增功能的必要。请记住,应该保持接口的简洁。(盲目地)扩增接口不会带来任何好处,只会引入噪音。我们要遵循 奥卡姆剃刀(Occam's Razor) 原则:如无必要,勿增实体。 现在我们已经基本完成了接口功能描述的工作,是时候改进实现了。

通过上一轮的迭代,系统已经能够处理购物框里有超过一件商品的情况了。现在我么来让系统具备购物框里有超过一件商品时计算总价的能力。首先写可执行期望:

  1. [Fact]

  2. public void Add2ItemsGrandTotal30() {

  3. var expectedGrandTotal = 30.00;

  4. var actualGrandTotal = 0.00;

  5. Assert.Equal(expectedGrandTotal, actualGrandTotal);

  6. }

硬编码所有值,尽量让期望测试失败。

测试确实失败了,现在得想办法让它通过。向购物框添加两件商品,然后调用CalculateGrandTotal()方法:

  1. [Fact]

  2. public void Add2ItemsGrandTotal30() {

  3. var expectedGrandTotal = 30.00;

  4. Hashtable item = new Hashtable();

  5. item.Add("00000001", 10.00);

  6. shoppingAPI.AddItem(item);

  7. Hashtable item2 = new Hashtable();

  8. item2.Add("00000002", 20.00);

  9. shoppingAPI.AddItem(item2);

  10. var actualGrandTotal = shoppingAPI.CalculateGrandTotal();

  11. Assert.Equal(expectedGrandTotal, actualGrandTotal);

  12. }

测试通过。现在共有六个可以通过的微测试,系统回到了稳态。

设定期望

作为一个认真负责的工程师,你希望确保当用户向购物框添加一些商品然后又移除一些商品后系统仍然能够计算出正确出总价。下面是这个新的期望:

  1. [Fact]

  2. public void Add2ItemsRemoveFirstItemGrandTotal200() {

  3. var expectedGrandTotal = 200.00;

  4. var actualGrandTotal = 0.00;

  5. Assert.Equal(expectedGrandTotal, actualGrandTotal);

  6. }

这个期望表示将两件商品加入到购物框,然后移除第一件后期望的总价是 ¥200。硬编码行为失败了。现在设计更具体的正面测试样例,然后运行代码:

  1. [Fact]

  2. public void Add2ItemsRemoveFirstItemGrandTotal200() {

  3. var expectedGrandTotal = 200.00;

  4. Hashtable item = new Hashtable();

  5. item.Add("00000001", 100.00);

  6. shoppingAPI.AddItem(item);

  7. Hashtable item2 = new Hashtable();

  8. item2.Add("00000002", 200.00);

  9. shoppingAPI.AddItem(item2);

  10. shoppingAPI.RemoveItem(item);

  11. var actualGrandTotal = shoppingAPI.CalculateGrandTotal();

  12. Assert.Equal(expectedGrandTotal, actualGrandTotal);

  13. }

在这个正面测试样例中,先向购物框加入第一件商品(编号为 00000001,价格为 ¥100),再加入第二件商品(编号为 00000002,价格为 ¥200)。然后将第一件商品移除,计算总价,比较计算值与期望值是否相等。

运行期望测试,系统正确地计算出了总价,满足这个期望测试。现在有七个能顺利通过的测试了。系统运行良好,无异常!

  1. Test Run Successful.

  2. Total tests: 7

  3. Passed: 7

  4. Total time: 0.9544 Seconds

敬请期待

现在你已经学习了 ZOMBIES 方法中的 ZOMBI 部分,下一篇文章将介绍处理特殊行为。到那个时候,你可以试试自己的测试!

(题图:MJ/c4eb23b5-84aa-4477-a6b9-7d2a6d1aeee4)

via:

作者: 选题: 译者: 校对:

本文由 原创编译, 荣誉推出

LCTT 译者 :Xiangbin Ma

翻译: 6.0 篇

贡献: 3254 天

2014-07-01

2023-05-29

https://linux.cn/lctt/toknow-gh

欢迎遵照 CC-BY-SA 协议规定转载,

如需转载,请在文章下留言 “ 转载:公众号名称”,

我们将为您添加白名单,授权“ 转载文章时可以修改”。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
最新情况!前京东副总裁蔡磊5月10日转入协和ICU,曾经考虑气切!

最新情况!前京东副总裁蔡磊5月10日转入协和ICU,曾经考虑气切!

谈娱新语
2024-06-14 22:02:42
不是杨鸣,不是郭士强!男篮主教练基本确定,球迷都怕挨骂

不是杨鸣,不是郭士强!男篮主教练基本确定,球迷都怕挨骂

十点体坛
2024-06-14 22:38:49
汪峰给森林北写歌,歌词肉麻遭吐槽,森林北给汪峰做新疆大盘鸡

汪峰给森林北写歌,歌词肉麻遭吐槽,森林北给汪峰做新疆大盘鸡

红大娘娱乐
2024-05-30 21:23:45
真主党二号人物被击毙?以色列发动18年来最大打击

真主党二号人物被击毙?以色列发动18年来最大打击

项鹏飞
2024-06-14 14:53:40
中央决定:庞骁刚任国家电网总经理

中央决定:庞骁刚任国家电网总经理

新京报
2024-06-15 00:01:10
一男子怀疑妻子出轨,特意带她出国体验异域按摩,结果双方沦陷

一男子怀疑妻子出轨,特意带她出国体验异域按摩,结果双方沦陷

亲爱的落落
2024-05-07 16:58:09
广东男篮两小将申请离队,总计六人离开,下赛季本土大名单浮出!

广东男篮两小将申请离队,总计六人离开,下赛季本土大名单浮出!

体坛热消息
2024-06-15 08:22:16
毛主席开口保人,许世友沉默拒绝,毛叹道:你对我的感情不深了

毛主席开口保人,许世友沉默拒绝,毛叹道:你对我的感情不深了

今人说古
2024-06-14 23:55:03
陈文清主持召开专题会议部署扫黑除恶斗争

陈文清主持召开专题会议部署扫黑除恶斗争

新京报
2024-06-14 22:57:12
极简手机 Light Phone III 发布:时隔 6 年回归,399 美元

极简手机 Light Phone III 发布:时隔 6 年回归,399 美元

IT之家
2024-06-14 13:44:07
沙特背刺中方?2000亿基建项目绕过中国,中国乐了:结果反转!

沙特背刺中方?2000亿基建项目绕过中国,中国乐了:结果反转!

小陆搞笑日常
2024-06-14 19:44:12
后续!查网约车拦住乘客不让走态度还恶劣!通报出!那名制服没提

后续!查网约车拦住乘客不让走态度还恶劣!通报出!那名制服没提

金哥说新能源车
2024-06-14 22:00:49
北京部分典当行报价:飞天茅台典当金额触及1499元/瓶

北京部分典当行报价:飞天茅台典当金额触及1499元/瓶

经济观察报
2024-06-14 22:10:12
广东男篮人员调整!2大主力被交易,杜锋心腹离队,新下家确定

广东男篮人员调整!2大主力被交易,杜锋心腹离队,新下家确定

十点体坛
2024-06-14 22:40:10
援乌的背后是“收益”, 特朗普怎么看?

援乌的背后是“收益”, 特朗普怎么看?

直新闻
2024-06-14 12:33:08
36岁女护士新婚夜,对丈夫说:你的东西就是我的东西!

36岁女护士新婚夜,对丈夫说:你的东西就是我的东西!

娱乐洞察点点
2024-06-14 18:08:29
中国发出警告:90天内不支付358亿赔偿金,18艘军舰就别想要了

中国发出警告:90天内不支付358亿赔偿金,18艘军舰就别想要了

星辰故事屋
2024-06-09 17:09:59
哈佛要找她聊一聊,浙大按规定办事,姜萍的横空出世打了谁的脸?

哈佛要找她聊一聊,浙大按规定办事,姜萍的横空出世打了谁的脸?

小怪吃美食
2024-06-14 22:09:16
两性疑问:为什么男生更喜欢从后面来

两性疑问:为什么男生更喜欢从后面来

坟头长草
2024-05-30 16:33:38
太现实,中专生姜萍数学大赛成绩超麻省理工学生,只能上常熟理工

太现实,中专生姜萍数学大赛成绩超麻省理工学生,只能上常熟理工

育学笔谈
2024-06-14 11:18:52
2024-06-15 11:04:49
Linux
Linux
Linux 中国开源社区
8016文章数 73123关注度
往期回顾 全部

科技要闻

TikTok开始找退路了?

头条要闻

美官员:华盛顿希望看到G7在对抗中国时达到空前团结

头条要闻

美官员:华盛顿希望看到G7在对抗中国时达到空前团结

体育要闻

我们为什么还爱欧洲杯?

娱乐要闻

江宏杰秀儿女刺青,不怕刺激福原爱?

财经要闻

新情况!高层对人民币的态度180°转弯

汽车要闻

提供100/240kW双电机版本车型 乐道L60实车曝光

态度原创

家居
亲子
时尚
游戏
军事航空

家居要闻

空谷来音 朴素留白的侘寂之美

亲子要闻

萌娃一生气就乱扔东西 还是他爹有招 网友:小孩必须从小就管

谁说50岁只能穿老年装?看看火遍时尚圈的“新搭配”,太洋气

《夺宝奇兵》瞄准更广泛的受众 潜行和解密为重点

军事要闻

美国与乌克兰签署双边安全协议

无障碍浏览 进入关怀版