偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

ZOMBIES:在軟件開(kāi)發(fā)中定義邊界和接口(三)

開(kāi)發(fā)
喪尸是沒(méi)有邊界感的,需要為你的軟件設(shè)定限制和期望。

喪尸沒(méi)有邊界感。它們踩倒柵欄,推倒圍墻,進(jìn)入不屬于它們的地盤。在前面的文章中,我已經(jīng)解釋了為什么把所有編程問(wèn)題當(dāng)作一群?jiǎn)适淮涡蕴幚硎清e(cuò)誤的。

ZOMBIES 代表首字母縮寫:

  • Z – 最簡(jiǎn)場(chǎng)景(Zero)
  • O – 單個(gè)元素場(chǎng)景(One)
  • M – 多個(gè)元素場(chǎng)景(Many or more complex)
  • B – 邊界行為(Boundary behaviors)
  • I – 接口定義(Interface definition)
  • E – 處理特殊行為(Exercise exceptional behavior)
  • S – 簡(jiǎn)單場(chǎng)景用簡(jiǎn)單的解決方案(Simple scenarios, simple solutions)

在本系列的前面兩篇文章中,我演示了 ZOMBIES 方法的前三部分:最簡(jiǎn)場(chǎng)景、單元素場(chǎng)景和多元素場(chǎng)景。第一篇文章 實(shí)現(xiàn)了最簡(jiǎn)場(chǎng)景,它提供了代碼中的最簡(jiǎn)可行路徑。第二篇文章中針對(duì)單元素場(chǎng)景和多元素場(chǎng)景 運(yùn)行測(cè)試。在這篇文章中,我將帶你了解邊界和接口。

回到單元素場(chǎng)景

要想處理邊界,你需要繞回來(lái)(迭代)。

首先思考下面的問(wèn)題:電子商務(wù)的邊界是什么?我需要限制購(gòu)物框的大小嗎?(事實(shí)上,我不認(rèn)為這有任何意義。)

目前唯一合理的邊界條件是確保購(gòu)物框里的商品數(shù)量不能為負(fù)數(shù)。將這個(gè)限制表示成可運(yùn)行的期望:

[Fact]
public void Add1ItemRemoveItemRemoveAgainHas0Items() {
        var expectedNoOfItems = 0;
        var actualNoOfItems = -1;
        Assert.Equal(expectedNoOfItems, actualNoOfItems);
}

這就是說(shuō),如果你向購(gòu)物框里添加一件商品,然后將這個(gè)商品移除兩次,shoppingAPI 的實(shí)例應(yīng)該告訴你購(gòu)物框里有零個(gè)商品。

當(dāng)然這個(gè)可運(yùn)行期望(微測(cè)試)不出意料地會(huì)失敗。想要這個(gè)微測(cè)試能夠通過(guò),最小改動(dòng)是什么呢?

[Fact]
public void Add1ItemRemoveItemRemoveAgainHas0Items() {
        var expectedNoOfItems = 0;
        Hashtable item = new Hashtable();
        shoppingAPI.AddItem(item);
        shoppingAPI.RemoveItem(item);
        var actualNoOfItems = shoppingAPI.RemoveItem(item);
        Assert.Equal(expectedNoOfItems, actualNoOfItems);
}

這個(gè)期望測(cè)試依賴于 RemoveItem(item) 功能。目前的 shippingAPI 還不具備該功能,你需要增加該功能。

回到 app 文件夾,打開(kāi) IShippingAPI.cs 文件,新增以下聲明:

int RemoveItem(Hashtable item);

到 ShippingAPI.cs 中實(shí)現(xiàn)該功能:

public int RemoveItem(Hashtable item) {
        basket.RemoveAt(basket.IndexOf(item));
        return basket.Count;
}

運(yùn)行,然后你會(huì)得到如下錯(cuò)誤:

Error

Error

系統(tǒng)在移除一個(gè)不在購(gòu)物框的商品,這導(dǎo)致了系統(tǒng)崩潰。加一點(diǎn)點(diǎn) 防御式編程defensive programming:

public int RemoveItem(Hashtable item) {
        if(basket.IndexOf(item) >= 0) {
                basket.RemoveAt(basket.IndexOf(item));
        }
        return basket.Count;
}

在移除商品之前先檢查它是否在購(gòu)物框中。(你可能試過(guò)用捕獲異常的方式來(lái)處理,但是我認(rèn)為上面的處理方式更具可讀性。)

更多具體的期望

在講更多具體的期望之前,讓我們先探討一下什么是接口。在軟件工程中,接口表示一種規(guī)范,或者對(duì)能力的描述。從某種程度上來(lái)說(shuō),接口類似于菜譜。它羅列出了制作蛋糕的原材料,但它本身并不能吃。我們只是按照菜譜上的說(shuō)明來(lái)烤蛋糕。

與此類似,我們首先通過(guò)說(shuō)明這個(gè)服務(wù)能做什么的方式來(lái)定義我們的服務(wù)。這個(gè)描述說(shuō)明就是所謂的接口。但是接口本身并不能向我們提供任何功能。它只是指導(dǎo)我們實(shí)現(xiàn)指定功能的藍(lán)圖而已。

到目前為止,我們已經(jīng)實(shí)現(xiàn)了接口(只是某部分實(shí)現(xiàn)了,稍后還會(huì)增加新功能)和業(yè)務(wù)處理邊界(也就是購(gòu)物框里的商品不能是負(fù)數(shù))。你指導(dǎo)了 shoppingAPI 怎么向購(gòu)物框添加商品,并通過(guò) Add2ItemsBasketHas2Items 測(cè)試驗(yàn)證了該功能的有效性。

然而僅僅具備向購(gòu)物框添加商品的功能還不足以使其成為一個(gè)網(wǎng)購(gòu)應(yīng)用程序。它還需要能夠計(jì)算購(gòu)物框里的商品的總價(jià)?,F(xiàn)在需要增加另一個(gè)期望。

按照慣例,從最直接明了的期望開(kāi)始。當(dāng)你向購(gòu)物框里加入一件價(jià)值 ¥10 的商品時(shí),你希望這個(gè)購(gòu)物 API 能正確地計(jì)算出總價(jià)為 ¥10。

第五個(gè)測(cè)試(偽造版)如下:

[Fact]
public void Add1ItemPrice10GrandTotal10() {
        var expectedTotal = 10.00;
        var actualTotal = 0.00;
        Assert.Equal(expectedTotal, actualTotal);
}

還是一樣的老把戲,通過(guò)硬編碼一個(gè)錯(cuò)誤的值讓 Add1ItemPrice10GrandTotal10 測(cè)試失敗。當(dāng)然前三個(gè)測(cè)試成功通過(guò),但第四個(gè)新增的測(cè)試失敗了:

A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.57] tests.UnitTest1.Add1ItemPrice10GrandTotal10 [FAIL]
  X tests.UnitTest1.Add1ItemPrice10GrandTotal10 [4ms]
  Error Message:
   Assert.Equal() Failure
Expected: 10
Actual: 0

Test Run Failed.
Total tests: 4
     Passed: 3
         Failed: 1
 Total time: 1.0320 Seconds

將硬編碼值換成實(shí)際的處理代碼。首先,檢查接口是否具備計(jì)算訂單總價(jià)的功能。根本沒(méi)有這種東西。目前為止接口中只聲明了三個(gè)功能:

  1. int NoOfItems();
  2. int AddItem(Hashtable item);
  3. int RemoveItem(Hashtable item);

它們都不具備計(jì)算總價(jià)的能力。所以需要聲明一個(gè)新功能:

double CalculateGrandTotal();

這個(gè)新功能應(yīng)該讓 shoppingAPI 具備計(jì)算總價(jià)的能力。這是通過(guò)遍歷購(gòu)物框中的商品并把它們的價(jià)格累加起來(lái)實(shí)現(xiàn)的。

修改第五個(gè)測(cè)試:

[Fact]
public void Add1ItemPrice10GrandTotal10() {
        var expectedGrandTotal = 10.00;
        Hashtable item = new Hashtable();
        item.Add("00000001", 10.00);
        shoppingAPI.AddItem(item);
        var actualGrandTotal = shoppingAPI.CalculateGrandTotal();
        Assert.Equal(expectedGrandTotal, actualGrandTotal);
}

這個(gè)測(cè)試表明了這樣的期望:如果向購(gòu)物框里加入一件價(jià)格 ¥10 的商品,然后調(diào)用 CalculateGrandTotal() 方法,它會(huì)返回商品總價(jià) ¥10。這是一個(gè)完全合理的期望,它完全符合商品總價(jià)計(jì)算的邏輯。

那么怎么實(shí)現(xiàn)這個(gè)功能呢?就像以前一樣,先寫一個(gè)假的實(shí)現(xiàn)?;氐?nbsp;ShippingAPI 類中,實(shí)現(xiàn)在接口中聲明的 CalculateGrandTotal() 方法:

public double CalculateGrandTotal() {
                return 0.00;
}

現(xiàn)在先將返回值硬編碼為 0.00,只是為了檢驗(yàn)這個(gè)測(cè)試能否正常運(yùn)行,并確認(rèn)它是能夠失敗的。事實(shí)上,它能夠運(yùn)行,并且如預(yù)期一樣失敗。接下來(lái)的工作就是正確實(shí)現(xiàn)計(jì)算商品總價(jià)的處理邏輯:

public double CalculateGrandTotal() {
        double grandTotal = 0.00;
        foreach(var product in basket) {
                Hashtable item = product as Hashtable;
                foreach(var value in item.Values) {
                        grandTotal += Double.Parse(value.ToString());
                }
        }
        return grandTotal;
}

運(yùn)行,五個(gè)測(cè)試全部通過(guò)!

從單元素場(chǎng)景到多元素場(chǎng)景

現(xiàn)在是時(shí)候進(jìn)入下一輪迭代了。你已經(jīng)通過(guò)處理最簡(jiǎn)場(chǎng)景、單元素場(chǎng)景和邊界場(chǎng)景迭代地構(gòu)建了系統(tǒng),現(xiàn)在需要處理稍復(fù)雜的多元素場(chǎng)景了。

快捷提示:由于我們一直在針對(duì)單個(gè)元素場(chǎng)景、多元素場(chǎng)景和邊界行為這三點(diǎn)上對(duì)軟件進(jìn)行迭代改進(jìn),一些讀者可能會(huì)認(rèn)為我們同樣應(yīng)該對(duì)接口進(jìn)行改進(jìn)。我們稍后就會(huì)發(fā)現(xiàn),接口已經(jīng)完全滿足需要了,目前沒(méi)有新增功能的必要。請(qǐng)記住,應(yīng)該保持接口的簡(jiǎn)潔。(盲目地)擴(kuò)增接口不會(huì)帶來(lái)任何好處,只會(huì)引入噪音。我們要遵循 奧卡姆剃刀Occam's Razor 原則:如無(wú)必要,勿增實(shí)體。 現(xiàn)在我們已經(jīng)基本完成了接口功能描述的工作,是時(shí)候改進(jìn)實(shí)現(xiàn)了。

通過(guò)上一輪的迭代,系統(tǒng)已經(jīng)能夠處理購(gòu)物框里有超過(guò)一件商品的情況了?,F(xiàn)在我么來(lái)讓系統(tǒng)具備購(gòu)物框里有超過(guò)一件商品時(shí)計(jì)算總價(jià)的能力。首先寫可執(zhí)行期望:

[Fact]
public void Add2ItemsGrandTotal30() {
        var expectedGrandTotal = 30.00;
        var actualGrandTotal = 0.00;
        Assert.Equal(expectedGrandTotal, actualGrandTotal);
}

硬編碼所有值,盡量讓期望測(cè)試失敗。

測(cè)試確實(shí)失敗了,現(xiàn)在得想辦法讓它通過(guò)。向購(gòu)物框添加兩件商品,然后調(diào)用 CalculateGrandTotal() 方法:

[Fact]
public void Add2ItemsGrandTotal30() {
	      var expectedGrandTotal = 30.00;
        Hashtable item = new Hashtable();
        item.Add("00000001", 10.00);
        shoppingAPI.AddItem(item);
        Hashtable item2 = new Hashtable();
        item2.Add("00000002", 20.00);
        shoppingAPI.AddItem(item2);
        var actualGrandTotal = shoppingAPI.CalculateGrandTotal();
        Assert.Equal(expectedGrandTotal, actualGrandTotal);
}

測(cè)試通過(guò)。現(xiàn)在共有六個(gè)可以通過(guò)的微測(cè)試,系統(tǒng)回到了穩(wěn)態(tài)。

設(shè)定期望

作為一個(gè)認(rèn)真負(fù)責(zé)的工程師,你希望確保當(dāng)用戶向購(gòu)物框添加一些商品然后又移除一些商品后系統(tǒng)仍然能夠計(jì)算出正確出總價(jià)。下面是這個(gè)新的期望:

[Fact]
public void Add2ItemsRemoveFirstItemGrandTotal200() {
        var expectedGrandTotal = 200.00;
        var actualGrandTotal = 0.00;
        Assert.Equal(expectedGrandTotal, actualGrandTotal);
}

這個(gè)期望表示將兩件商品加入到購(gòu)物框,然后移除第一件后期望的總價(jià)是 ¥200。硬編碼行為失敗了?,F(xiàn)在設(shè)計(jì)更具體的正面測(cè)試樣例,然后運(yùn)行代碼:

[Fact]
public void Add2ItemsRemoveFirstItemGrandTotal200() {
        var expectedGrandTotal = 200.00;
        Hashtable item = new Hashtable();
        item.Add("00000001", 100.00);
        shoppingAPI.AddItem(item);
        Hashtable item2 = new Hashtable();
        item2.Add("00000002", 200.00);
        shoppingAPI.AddItem(item2);
        shoppingAPI.RemoveItem(item);
        var actualGrandTotal = shoppingAPI.CalculateGrandTotal();
        Assert.Equal(expectedGrandTotal, actualGrandTotal);
}

在這個(gè)正面測(cè)試樣例中,先向購(gòu)物框加入第一件商品(編號(hào)為 00000001,價(jià)格為 ¥100),再加入第二件商品(編號(hào)為 00000002,價(jià)格為 ¥200)。然后將第一件商品移除,計(jì)算總價(jià),比較計(jì)算值與期望值是否相等。

運(yùn)行期望測(cè)試,系統(tǒng)正確地計(jì)算出了總價(jià),滿足這個(gè)期望測(cè)試?,F(xiàn)在有七個(gè)能順利通過(guò)的測(cè)試了。系統(tǒng)運(yùn)行良好,無(wú)異常!

Test Run Successful.
Total tests: 7
     Passed: 7
 Total time: 0.9544 Seconds

敬請(qǐng)期待

現(xiàn)在你已經(jīng)學(xué)習(xí)了 ZOMBIES 方法中的 ZOMBI 部分,下一篇文章將介紹處理特殊行為。到那個(gè)時(shí)候,你可以試試自己的測(cè)試!

責(zé)任編輯:龐桂玉 來(lái)源: Linux中國(guó)
相關(guān)推薦

2023-05-16 17:34:49

ZOMBIES軟件開(kāi)發(fā)

2023-05-30 18:26:49

ZOMBIES軟件開(kāi)發(fā)

2023-05-13 17:38:03

ZOMBIES軟件開(kāi)發(fā)

2010-03-24 14:37:29

Python編程語(yǔ)言

2024-04-02 06:03:00

GenAI軟件開(kāi)發(fā)人工智能

2023-08-01 15:24:18

DevOps自動(dòng)化軟件開(kāi)發(fā)

2024-05-20 12:47:03

AI軟件開(kāi)發(fā)

2009-06-12 11:35:28

模式框架軟件設(shè)計(jì)

2023-06-08 16:47:09

軟件開(kāi)發(fā)工具

2023-02-09 16:48:12

軟件開(kāi)發(fā)測(cè)試結(jié)對(duì)測(cè)試

2024-07-23 08:06:19

緩存技術(shù)策略

2024-03-14 06:51:22

GenAI人工智能

2023-06-09 19:01:03

軟件開(kāi)發(fā)

2021-12-14 09:00:00

IT開(kāi)發(fā)運(yùn)營(yíng)

2020-03-31 10:08:15

零信任安全軟件

2011-07-04 17:09:54

2011-08-11 09:56:50

模式

2020-10-16 10:21:23

大數(shù)據(jù)開(kāi)發(fā)軟件開(kāi)發(fā)技術(shù)

2014-03-21 09:30:26

軟件開(kāi)發(fā)碼農(nóng)

2020-06-24 11:21:47

軟件開(kāi)發(fā)面試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)