自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

有了Bug,先看看“Type”

譯文 精選
開發(fā) 架構(gòu)
人工神經(jīng)網(wǎng)絡(luò)可以模仿或者代替與人的思維相關(guān)的功能,實(shí)現(xiàn)問題求解、問題自動(dòng)診斷,從而解決傳統(tǒng)方法所不能或難以解決的問題。

譯者 | 崔皓

策劃 | 云昭

1、開篇

不懂原始類型的程序員,往往由于急于求成,上手很快,最后卻發(fā)現(xiàn)被各種Bug耽誤進(jìn)度。本文以經(jīng)典的郵箱類型、貨幣類型、密碼類型為例,利用好類型系統(tǒng)能夠很好地改進(jìn)編碼方式,同時(shí)為技術(shù)人找回“打穩(wěn)地基”的快樂。

2、字符串類型變身成為郵箱類型

筆者已經(jīng)厭倦了使用原始類型,并試圖通過使用原始類型,來(lái)為一個(gè)領(lǐng)域進(jìn)行建模。

字符串值(String)類型不僅僅用來(lái)保存用戶的電子郵件地址或國(guó)籍信息,還可以有更豐富的用途。我需要一個(gè)EmailAddress的類型,并定義它不能為空,同時(shí)希望有單一的入口來(lái)創(chuàng)建該類型的對(duì)象。在返回一個(gè)值之前,需要被驗(yàn)證和規(guī)范化。

同時(shí),也希望該數(shù)據(jù)類型有一些方法,如.Domain()或.NonAliasValue(),在輸入foo+bar@gmail.com時(shí),會(huì)分別調(diào)用這兩個(gè)方法并返回gmail.com和foo@gmail.com。

在類型設(shè)計(jì)中應(yīng)該考慮這種有用的功能,該功能的引入有助于防止錯(cuò)誤的發(fā)生,并提高了類型的可維護(hù)性。

3、設(shè)計(jì)良好、功能實(shí)用的類型

例如,一個(gè)EmailAddress可以提供兩個(gè)方法來(lái)檢查是否相等。

lEquals方法用來(lái)判斷兩個(gè)(規(guī)范化)的電子郵件地址是否相同,如果相同將返回true。

lEqualsInPrinciple

該方法對(duì)于foo@gmail.com和foo+bar@gmail.com的輸入會(huì)判斷相同,因此也會(huì)返回true。(這里假設(shè)兩個(gè)郵箱都是同一個(gè)人注冊(cè)的,因此相同需要判斷兩個(gè)郵箱“相等”)

特定類型的方法在不同的使用場(chǎng)景下都會(huì)發(fā)揮不同的作用。如果用戶jane@gmail.com注冊(cè),但又用Jane@gmail.com登錄,那么用戶的登錄不應(yīng)該失?。▋H僅存在首字母大小寫的區(qū)別)。同樣的,如果戶用使用電子郵件地址(foo@gmail.com)和另一個(gè)注冊(cè)賬戶(foo+svc@gmail.com)聯(lián)系客戶支持,相同就需要對(duì)這兩個(gè)郵箱進(jìn)行有效匹配。這些都是典型應(yīng)用場(chǎng)景,如果沒有散落在代碼庫(kù)中的業(yè)務(wù)邏輯,僅憑一個(gè)簡(jiǎn)單的字符串是無(wú)法滿足的。

注意:根據(jù)Office RFC描述,電子郵件地址中@符號(hào)之前的部分可以區(qū)分大小寫,但所有主要的電子郵件主機(jī)都將其視為不區(qū)分大小寫,因此,域名類型也考慮這方面的問題。

4、好的類型可以防止Bug

順著上面郵箱類型的例子,如果我們想走得更遠(yuǎn),假如希望一個(gè)電子郵件地址可以被驗(yàn)證或未被驗(yàn)證。通常的做法是,通過向個(gè)人的收件箱發(fā)送一個(gè)獨(dú)特的代碼來(lái)驗(yàn)證電子郵件地址。這些 "商業(yè) "上的互動(dòng)也可以通過類型系統(tǒng)來(lái)表達(dá)。例如,創(chuàng)建一個(gè)叫做VerifiedEmailAddress的第二個(gè)類型。該類型可以繼承自EmailAddress。并且確保代碼中只有一個(gè)地方可以產(chǎn)生VerifiedEmailAddress的實(shí)例,即負(fù)責(zé)驗(yàn)證用戶地址的服務(wù)。如此這般,應(yīng)用程序的其他部分可以依靠這個(gè)新類型來(lái)防止Bug。

任何發(fā)送電子郵件的功能都可以依靠該類來(lái)驗(yàn)證的電子郵件地址的安全性。想象一下,如果電子郵件地址是通過簡(jiǎn)單的字符串來(lái)表達(dá)的,會(huì)是怎樣的情況。

因此,要找到相關(guān)的用戶賬戶,檢查一些模糊的標(biāo)志,如HasVerifiedEmail或IsActive,確保這些標(biāo)志設(shè)置是正確的,而不會(huì)在默認(rèn)構(gòu)造函數(shù)中被錯(cuò)誤地初始化為真。有太多的錯(cuò)誤空間由于使用了原始字符串,導(dǎo)致有些檢查不到位的情況,這種使用原始類型的表達(dá)方式被認(rèn)為是懶惰和缺乏想象力的編程。

5、富類型免受錯(cuò)誤的侵?jǐn)_

另一個(gè)很好的例子是貨幣!我已經(jīng)數(shù)不清有多少應(yīng)用程序使用十進(jìn)制來(lái)表達(dá)貨幣值。也已經(jīng)數(shù)不清有多少應(yīng)用程序使用十進(jìn)制類型表達(dá)貨幣值。為什么呢?

這種類型有很多問題,甚至很難理解。每個(gè)與錢打交道的領(lǐng)域都應(yīng)該有專門的貨幣類型。貨幣類型應(yīng)該包括貨幣和運(yùn)算符重載(或其他安全功能),以防止出現(xiàn)100美元與20英鎊相乘這樣的愚蠢錯(cuò)誤。此外,并非每種貨幣在小數(shù)點(diǎn)后都只有兩位數(shù)。有些貨幣,如巴林或科威特第納爾有三位。如果你在致力處理投資或銀行貸款,那么你最好確保你呈現(xiàn)的Unidad de Fomento有4個(gè)小數(shù)點(diǎn)。這些問題已經(jīng)很重要了,足以保證有一個(gè)專門的Moneytype,但這還遠(yuǎn)遠(yuǎn)不夠。

除非在系統(tǒng)內(nèi)部完成所有功能,否則就不得不與第三方系統(tǒng)打交道。例如,大多數(shù)支付網(wǎng)關(guān)都是以整數(shù)值來(lái)請(qǐng)求和響應(yīng)資金。由于整數(shù)值不能涵蓋類似浮點(diǎn)數(shù)(雙數(shù)類型)的四舍五入運(yùn)算,因此比浮點(diǎn)數(shù)更受歡迎。唯一需要注意的是,數(shù)值必須以小單位(如美分、便士、迪拉姆、格羅茲、科佩克等)傳輸,這意味著如果你的程序處理小數(shù)點(diǎn)數(shù)值,在與外部API對(duì)話時(shí),你將不得不不斷地來(lái)回轉(zhuǎn)換它們。如前所述,并不是每種貨幣都使用兩個(gè)小數(shù)點(diǎn),所以不是每次都是簡(jiǎn)單的乘/除以100。事情很快就會(huì)變得很困難,如果這些業(yè)務(wù)規(guī)則被封裝成一個(gè)簡(jiǎn)潔的單一類型,事情就會(huì)被大大簡(jiǎn)化。

var x = Money.FromMinorUnit(100, "GBP"):£1
var y = Money.FromUnit(100.50, "GBP"):£1.50
Console.WriteLine(x.AsUnit()):1.5
Console.WriteLine(x.AsMinorUnit()):150

如果這還不夠復(fù)雜的話,各國(guó)也有不同的貨幣格式來(lái)表示貨幣。在英國(guó),"一萬(wàn)英鎊和五十便士 "將被表示為10,000.50,但在德國(guó),"一萬(wàn)歐元和五十美分 "將被顯示為10.000,50。試想一下,如果這些規(guī)則沒有放到統(tǒng)一的貨幣類型中,那么在整個(gè)代碼庫(kù)中會(huì)有多少與貨幣相關(guān)的代碼被分割開來(lái)。

此外,一個(gè)專門的貨幣類型可以包括許多功能,這將使貨幣價(jià)值的工作變得輕而易舉。

var gbp = Currency.Parse("GBP");
var loc = Locale.Parse("Europe/London");
var money = Money.FromMinorUnit(1000050, gbp);
money.Format(loc) // ==> £10,000.50
money.FormatVerbose(loc) // ==> GBP 10,000.50
money.FormatShort(loc) // ==> £10k

當(dāng)然,建立這樣一個(gè)Money類型在開始的時(shí)候會(huì)有點(diǎn)費(fèi)勁,但是一旦它被實(shí)現(xiàn)并經(jīng)過測(cè)試,那么代碼庫(kù)的其他部分就可以帶來(lái)更大的安全性,并防止大多數(shù)的Bug的產(chǎn)生,否則這些Bug會(huì)隨著時(shí)間的推移而慢慢出現(xiàn)。即使像Money.FromUnit(decimal v, Currency c)或Money.FromMinorUnit(int v, Currency c)這樣的小功能看起來(lái)并不多,但它使參與連續(xù)開發(fā)的程序員能夠意識(shí)到,用戶輸入或外部API收到的值是否包含在其中,這樣可以在一開始就防止Bug的產(chǎn)生。

6、聰明的類型設(shè)計(jì)減少副作用

富類型的偉大之處在于,可以以任何的方式來(lái)塑造它們。這里展示另外一個(gè)例子,富類型如何將團(tuán)隊(duì)從巨大的操作開銷中拯救出來(lái),甚至防止安全漏洞。

相信很多系統(tǒng)中的代碼庫(kù)都有一個(gè)類似于字符串secretKey或字符串password的東西,它作為函數(shù)的參數(shù)。那么在什么情況下有可能出錯(cuò)呢?

如下(偽)代碼:

try
{
var userLogin = new UserLogin
{
Username = username
Password=password
}
var success = _loginService.TryAuthenticate(userLogin);
if(success)
RedirectToHomeScreen(userLogin)。
ReturnUnauthorized()。
}
catch (Exception ex)
{
Logger.LogError(ex, "User login failed for {login}", userLogin);
}

這里出現(xiàn)的問題是,如果在認(rèn)證過程中拋出一個(gè)異常,那么這個(gè)應(yīng)用程序?qū)⒂脩舻拿魑拿艽a寫入日志。當(dāng)然,這段代碼一開始就不應(yīng)該存在,這種情況會(huì)隨著時(shí)間的推移而發(fā)生。大多數(shù)這樣的錯(cuò)誤都是隨著時(shí)間的推移而逐步發(fā)生的。

最初,UserLogin類可以有一組不同的屬性,在最初的代碼審查中,這段代碼可能沒有問題。幾年后,有人可能修改了UserLogin類以包括明文密碼。這個(gè)功能甚至不會(huì)出現(xiàn)在代碼提交的差異中,因此會(huì)逃過代碼審查。于是就引入了安全漏洞。然而,如果引入一個(gè)富類型(專有類型),就可以避免類似錯(cuò)誤的發(fā)生。

在C#中(以這個(gè)語(yǔ)言為例),當(dāng)一個(gè)對(duì)象被寫入日志時(shí),ToString()方法會(huì)被自動(dòng)調(diào)用。有了這些知識(shí),我們就可以設(shè)計(jì)一個(gè)這樣的密碼類型。

public readonly record struct Password()
{
public override string ToString()
{
return "****"
}
public string Cleartext()
{
return _cleartext。
}
}

雖然是一個(gè)微小的變化,但在系統(tǒng)的任何地方都不可能意外地輸出一個(gè)明文密碼。這不是很好嗎?

當(dāng)然,在實(shí)際的認(rèn)證過程中,你可能仍然需要明文值,那么就需要通過非常明確的命名方法Cleartext()來(lái)實(shí)現(xiàn)的,所以對(duì)這個(gè)操作的敏感性沒有任何含糊,它自動(dòng)引導(dǎo)開發(fā)者有意和謹(jǐn)慎地使用這個(gè)方法。

處理用戶的PII(如國(guó)家保險(xiǎn)號(hào)、稅號(hào)等)也是同樣的原則。使用專門的類型對(duì)這些信息進(jìn)行建模。覆蓋默認(rèn)函數(shù),如.ToString()。ToString()的默認(rèn)函數(shù),并通過相應(yīng)的命名函數(shù)暴露敏感數(shù)據(jù)。你永遠(yuǎn)不會(huì)把PII泄露到日志和其他地方,以后可能需要一個(gè)巨大的操作來(lái)再次刷掉它。

小伎倆發(fā)揮了大作用!

7、形成習(xí)慣

每當(dāng)開發(fā)者處理那些有特殊規(guī)則、行為或敏感數(shù)據(jù)的時(shí)候,不妨考慮如何能通過創(chuàng)建一個(gè)顯式類型來(lái)幫助自己。

讓我們?cè)倥e一個(gè)密碼類型的例子,可以走得更遠(yuǎn)!

密碼在被存儲(chǔ)到數(shù)據(jù)庫(kù)之前會(huì)進(jìn)行散列計(jì)算,但這個(gè)哈希值不是一個(gè)簡(jiǎn)單的字符串。在某些時(shí)候,我們將不得不在登錄過程中把以前存儲(chǔ)的哈希值與新計(jì)算的哈希值進(jìn)行比較。但并不是每個(gè)開發(fā)人員都是安全專家,比較兩個(gè)哈希字符串可能會(huì)使代碼受到攻擊。

檢查兩個(gè)密碼哈希值是否相等的推薦方法是以非優(yōu)化的方式進(jìn)行。

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization) ]

private static bool ByteArraysEqual(byte[] a, byte[] b)
{
if(a == null &&b == null)
{
return true;
}
if(a == null || b == null || a.Length != b.Length)
{
return false;
}
var areSame = true;
for (var i = 0; i < a.Length; i++)
{
areSame &= (a[i] == b[i])。
}
return areSame。
}

注:代碼示例取自原始ASP.NET Core資源庫(kù)

因此,將這一特殊功能編碼為一個(gè)專門的類型才是合理的。

public readonly record struct PasswordHash
{
public override bool Equals(PasswordHash other)
{
return ByteArraysEqual(this.Bytes(), other.Bytes())。
}
}

如果一個(gè)PasswordHasher只返回PasswordHash類型的值,即使是對(duì)業(yè)務(wù)不太了解的開發(fā)者也會(huì)使用一種安全的形式來(lái)檢查相等。

在建立領(lǐng)域模型方面要考慮周全! 當(dāng)然,編程中的一切都沒有明確的對(duì)錯(cuò)之分,人們的個(gè)人使用情況總是有更多的細(xì)微差別,這些不是在一篇文章中所能表達(dá)的,但筆者建議是,考慮如何使類型系統(tǒng)對(duì)開發(fā)者的幫助很大。現(xiàn)在許多現(xiàn)代編程語(yǔ)言都有非常豐富的類型系統(tǒng),我們可能忽視了它們沒有利用好這些類型改進(jìn)編碼方式。

原文鏈接:https://dusted.codes/the-type-system-is-a-programmers-best-friend

譯者介紹

崔皓,51CTO社區(qū)編輯,資深架構(gòu)師,擁有18年的軟件開發(fā)和架構(gòu)經(jīng)驗(yàn),10年分布式架構(gòu)經(jīng)驗(yàn)。

責(zé)任編輯:武曉燕 來(lái)源: 51CTO技術(shù)棧
相關(guān)推薦

2021-06-24 05:40:28

Windows 10操作系統(tǒng)微軟

2020-02-03 15:56:12

機(jī)器學(xué)習(xí)人工智能計(jì)算機(jī)

2016-09-23 20:39:31

2019-06-14 14:30:33

HTTP3協(xié)議

2011-10-14 10:03:31

SiriAndroid語(yǔ)音短信

2009-08-10 09:40:20

私有云計(jì)算企業(yè)文化

2014-07-18 16:37:54

iBeacon

2015-09-21 11:12:29

應(yīng)用工作流SLA評(píng)估公有云

2022-06-07 17:07:22

物聯(lián)網(wǎng)制造業(yè)IOT

2019-08-07 17:17:54

華為

2018-01-25 15:01:53

程序員年終獎(jiǎng)

2017-06-09 15:30:26

HDR

2019-08-20 13:45:01

阿里巴巴面試Java

2021-11-27 12:08:49

網(wǎng)絡(luò)攻擊微軟網(wǎng)絡(luò)安全

2024-01-17 17:19:47

2019-03-25 22:15:16

程序員產(chǎn)品經(jīng)理開發(fā)工具

2019-05-13 09:01:13

程序員職責(zé)產(chǎn)品經(jīng)理

2021-02-06 14:02:44

大數(shù)據(jù)崗位招聘

2016-12-26 20:17:17

數(shù)據(jù)excel表效率

2019-01-02 16:10:09

點(diǎn)贊
收藏

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