防禦編程
『壹』 防禦式編程的相關實例
1. 在非法輸入(Invalid Inputs)中保護你的程序
一個好程序,在非法輸入的情況下,要麼什麼都不輸出,要麼輸出錯誤信息。有幾種方法來防止非法的輸入:
(1)檢查來自於外部資源(external sources)的所有數據的值,例如來源於網路的數據的值,來源於文件的數據的值。檢查的目的是保證數據值在一個允許的范圍內。
(2)檢查每一個常式(routine)的輸入參數值。
一旦非法輸入被發現,那麼應該根據情況進行處理。防禦式編程的最佳的形式是在一開始就不引入錯誤。
2. 斷言(Assertions)
一個斷言通常是一個常式(routines)或者一個宏(marcos)。每個斷言通常含有兩個參數:一個布爾表示式(a boolean expression)和一個消息(a message)。一個布爾表達式的反面表示了一個錯誤。C 標准庫提供了一個 assert 宏,它只帶有一個參數,用法如下:
assert(1 == 0); // 注意 boolean expression 不要加引號
使用 assert 宏,需要包含頭文件 cassert 或者assert.h,執行上面語句的結果是程序終止運行,輸出與下面消息類似的消息:
Assertion failed: 1 == 0, file d:我的文檔visual studio projectslearningassertassert.cpp, line 9
通常來說,我們會定義自己的 assert 宏,其目的有兩個:
(1)新增參數,例如新增一個消息參數,使得 assert 宏輸出更為豐富的信息。
(2)改變 assert 的行為內容。C 標准庫中的 assert 宏將中斷程序,實際上,我們可以讓程序繼續運行而不中斷或者進入調試狀態等,另外還可以控制消息輸出的目標,即控制消息是輸出到控制台還是文本文件,甚至是通過網路發出。
下面是一個 C++ 實現的斷言:
#ifdef _DEBUG
#define Assert(exp, message)
{
if (!(exp))
{
std::cout << Assertion failed: << #exp <<
<< Message: << message <<
<< line: << __LINE__ <<
<< file: << __FILE__ <<
;
exit(EXIT_FAILURE);
}
}
#else
#define Assert(exp, message)
#endif
執行 Assert(1 == 0, Error); 結果為:
Assertion failed: 1 == 0
Message: Error
line: 24
file: d:我的文檔visual studio projectslearningassertassert.cpp
使用斷言應該注意一下的幾個問題:
1)對非預期錯誤使用斷言
斷言中的布爾表達式的反面一定要描述一個非預期錯誤,下面所述的在一定情況下為非預期錯誤的一些例子:
(1)空指針。
(2)輸入或者輸出參數的值不在預期范圍內。
(3)數組的越界。
非預期錯誤對應的就是預期錯誤,我們通常使用錯誤處理代碼來處理預期錯誤,而使用斷言處理非預期錯誤。在代碼執行過程中,有些錯誤永遠不應該發生,這樣的錯誤是非預期錯誤。斷言可以被看成是一種可執行的注釋,你不能依賴它來讓代碼正常工作(《Code Complete 2》)。例如:
int nRes = f(); // nRes 由 f 函數控制, f 函數保證返回值一定在 -100 ~ 100
Assert(-100 <= nRes && nRes <= 100); // 斷言,一個可執行的注釋
由於 f 函數保證了返回值處於 -100 ~ 100,那麼如果出現了 nRes 不在這個范圍的值時,就表明一個非預期錯誤的出現。後面會講到「隔欄」,那時會對斷言有更加深刻的理解。
2)不要把需要執行的代碼放入斷言中
斷言用於軟體的開發和維護,而通常不在發行版本中包含斷言。
需要執行的代碼放入斷言中是不正確的,因為在發行版本中,這些代碼通常不會被執行,例如:
Assert(f()); // f 函數通常在發行版本中不會被執行
而使用如下方法則比較安全:
res = f();
Assert(res); // 安全
3)對來源於內部系統的可靠的數據使用斷言,而不要對外部不可靠的數據使用斷言,對於外部不可靠數據,應該使用錯誤處理代碼。再次強調,把斷言看成可執行的注釋。
前條件(preconditions)和後條件(postconditions)
前條件是調用方代碼在調用常式(routines)或者實例化對象之前要確保為真的條件,後條件是常式執行後或者類實例化後應滿足的條件。下面是一個例子:
// 前條件,這里 nNum1 和 nNum2 的取值被前面代碼所約束並保證取值在 -50 ~ 50
Assert(-50 <= nNum1 && nNum1 <= 50, Add_nNum1);
Assert(-50 <= nNum2 && nNum2 <= 50, Add_nNum2);
int nRes = add(nNum1, nNum2);
// 後條件
Assert(-100 <= nRes && nRes <= 100, Add_nRes);
注意,由於 nNum1 和 nNum2 取值范圍已經被約束,因此可以使用斷言,但是如果 nNum1 和 nNum2 的值來源於不可靠的外部系統,那麼應該使用錯誤處理代碼,而不是使用斷言。
3. 錯誤處理技術
這里主要講述如何處理預期錯誤。
(1)終止程序運行
有些錯誤非常嚴重,如果出現,那麼最好就的做法就是讓程序終止並且讓用戶重啟程序。例如,對於顯示 X 光片的繪圖程序,如果數據出錯,那麼就關閉程序,這個時候關閉程序要遠遠好於顯示錯誤的數據。
(2)繼續程序運行
有時候,錯誤出現了,但是沒有必要去關閉程序,那麼就有兩種處理方案:
a. 在常式中處理錯誤
例如讓常式返回一個中立值,這是一種可行的方法,中立值在有些語言裡面被描述為「類型的默認值」,例如整型的中立值為 0,指針的中立值為 NULL(或 null 等)
b. 在常式外處理錯誤
返回一個錯誤碼也是可行的,返回錯誤碼意味著,錯誤將交由其他程序部分來處理,而不是本常式處理。
對於出現了錯誤,而沒有終止程序的運行,這時候,你可以在日誌文件中添加一個警告信息。
抉擇:正確性和健壯性
有些程序要求非常高的正確性,而有些程序要求較高的健壯性,通常兩者我們只能取其一。
(1)正確性意味著結果永遠是正確的,如果出錯,寧願不給出結果也不要給定一個不準確的值。
(2)健壯性意味著通過一些措施,保證軟體能夠正常運行下去,即使有時候會有一些不準確的值出現。
4. 隔欄(barricades)
隔欄本身就是一組錯誤處理代碼,對於內部類只需要使用斷言而無需使用錯誤處理代碼。當斷言為假時,表明了問題出在了程序中而不是數據中,需要通過修改代碼來消除問題。在此,請讀者聯系本文開始 --- 「在非法輸入(Invalid Inputs)中保護你的程序」這一部分進行思考。
『貳』 關於單片機編寫程序 將A中的二進制數變換成3為BCD碼 並將百,十,個位數分別防禦內部RAM的50H 51H 52H中
這程序也寫的太復雜了!既然51單片機有DIV指令,並且只是把A中的數字轉變為BCD,那就用DIV直接運算:
MOV A,#0FDH
MOV B,#100
DIV AB
MOV 50H,A
MOV A,B
MOB B,#10
DIV AB
MOV 51H,A
MOV 52H,B
計算完成,這樣50H就是百位數,51H是十位數,52H是個位數
『叄』 用java編程。實現兩個人對決。有血量有防禦。有攻擊力
幾天前有人提了個類似的問題,我當時寫了,但是忘記了沒有回復。現在發給你參考參考
<!----攻擊力=攻擊+隨機運氣暴擊---->
<!-----防禦力能抵制等量的傷害----->
<!----運氣值決定回血量和回血次數--->
*********lucy和jcak進入了角斗場*********
lucy初始狀態:血:1000攻:290防:60運氣:8
jcak初始狀態:血:1200攻:200防:120運氣:10
===========第1回合=============
lucy對jcak造成了362點傷害
運氣事件:jcak吃了血瓶增加了30點生命值
jcak對lucy造成了250點傷害
===========第2回合=============
lucy對jcak造成了322點傷害
jcak對lucy造成了260點傷害
運氣事件:lucy吃了血瓶增加了35點生命值
===========第3回合=============
lucy對jcak造成了362點傷害
運氣事件:jcak吃了血瓶增加了15點生命值
jcak對lucy造成了270點傷害
運氣事件:lucy吃了血瓶增加了15點生命值
===========第4回合=============
lucy對jcak造成了338點傷害
jcak對lucy造成了280點傷害
運氣事件:lucy吃了血瓶增加了5點生命值
===========第5回合=============
lucy對jcak造成了298點傷害
運氣事件:jcak吃了血瓶增加了45點生命值
jcak對lucy造成了260點傷害
===========第6回合=============
lucy對jcak造成了346點傷害
lucy取得了勝利
參考代碼。在附件
『肆』 小白准備轉行學習前端,有大神可以提一些建議嗎
學習是以興趣為前提的,你要對你所要學的內容產生興趣,這樣你才會花心思去學習。這和是不是小白沒關系的,對於小白而言,在學習過程中就需要更努力,多花時間和心思沒有什麼是學不會的。
自學方法:
1、作為一個初學者,你必須明確系統的學習方案,我建議一定有一個指導的人,全靠自己學,放棄的幾率非常大,在你對於web前端還沒有任何概念的時候,需要一個人領進門,之後就都靠自己鑽研,第一步就是確定web前端都需要哪些內容,並且在多少時間內學完,建議時間6個月保底。
2、視頻為主,書為輔。很多初學者在學習前端的時候非常喜歡去買書,但是最後的結果是什麼?看來看去什麼都不會寫,所以在這里給大家提醒,書可以看,但是是在建立於你已經對於某個知識點有了具體操作的執行後,在用書去鞏固概念,這樣更加利於你對於知識的理解。
3、對於學習技術來講,掌握一個學習方法是非常重要的,其實對於學習web前端來講,學習方法確實很多都是相通的,一旦學習方法不對,可能就會造成「方法不對,努力白費」。其實關於這方面還是很多的,我就簡單說個例子,有的人邊聽課邊跟著敲代碼,這樣就不對,聽課的時候就專心聽,做題的時候就專心做題,這都是過來人的經驗,一定要聽。根據每個人的不同,可能學習方法也會有所出路,找到適合你自己的學習法方法是學習的前提。
4、不建議自己一個人瞎學,在我了解學習編程的這些人來看,從零基礎開始學並且最後成功做這份工作的其實並沒有幾個,我覺得大部分原因就是因為他們都不了解web前端是干什麼的,學什麼的,就盲目的買書看,到處找視頻看,最後看著看著就放棄了,所以我建議初學者在沒有具體概念之前,還是找有經驗的人請教一下,聊過之後你就會知道web前端具體是干什麼的,該怎麼學,這是我個人的小建議,可以不採納。
自學路線:
第1階段:前端頁面重構(4周)
內容包含了:(PC端網站布局項目、HTML5+CSS3基礎項目、WebApp頁面布局項目)
第2階段:JavaScript高級程序設計(5周)
內容包含:(原生JavaScript交互功能開發項目、面向對象進階與ES5/ES6應用項目、JavaScript工具庫自主研發項目)
第3階段:PC端全棧項目開發(3周)
內容包含:(jQuery經典交互特效開發、HTTP協議、Ajax進階與PHP/JAVA開發項目、前端工程化與模塊化應用項目、PC端網站開發項目、PC端管理信息系統前端開發項目)
第4階段:移動端項目開發(6周)
內容包含:(Touch端項目、微信場景項目、應用Angular+Ionic開發WebApp項目、應用Vue.js開發WebApp項目、應用React.js開發WebApp項目)
第5階段:混合(Hybrid,ReactNative)開發(1周)
內容包含:(微信小程序開發、ReactNative、各類混合應用開發)
第6階段:NodeJS全棧開發(1周)
內容包括:(WebApp後端系統開發、一、NodeJS基礎與NodeJS核心模塊二、Express三、noSQL資料庫)
視頻教程:
網頁鏈接
網頁鏈接
如果你對於學習前端有任何不懂的可以隨時來問我,如果沒有比較好的教程,也可以問我要。
『伍』 pthon 中獲取資料庫中的值用於斷言中時報錯,該怎麼解決,具體信息如下:
這個問題是如何在一些場景下使用斷言表達式,通常會有人誤用它,所以我決定寫一篇文章來說明何時使用斷言,什麼時候不用。
為那些還不清楚它的人,Python的assert是用來檢查一個條件,如果它為真,就不做任何事。如果它為假,則會拋出AssertError並且包含錯誤信息。例如:
py> x = 23
py> assert x > 0, "x is not zero or negative"
py> assert x%2 == 0, "x is not an even number"
Traceback (most recent call last):
File "", line 1, in
AssertionError: x is not an even number
很多人用assert作為一個很快和容易的方法來在參數錯誤的時候拋出異常。但這樣做是錯的,非常錯誤,有兩個原因。首先AssertError不是在測試參數時應該拋出的錯誤。你不應該像這樣寫代碼:
if not isinstance(x, int):
raise AssertionError("not an int")
你應該拋出TypeError的錯誤,assert會拋出錯誤的異常。
但是,更危險的是,有一個關於assert的困擾:它可以被編譯好然後從來不執行,如果你用 –O 或 –oo
選項運行Python,結果不保證assert表達式會運行到。當適當的使用assert時,這是未來,但是當assert不恰當的使用時,它會讓代碼用
-O執行時出錯。
那什麼時候應該使用assert?沒有特定的規則,斷言應該用於:
防禦型的編程
運行時檢查程序邏輯
檢查約定
程序常量
檢查文檔
(在測試代碼的時候使用斷言也是可接受的,是一種很方便的單元測試方法,你接受這些測試在用-O標志運行時不會做任何事。我有時在代碼里使用
assert False來標記沒有寫完的代碼分支,我希望這些代碼運行失敗。盡管拋出NotImplementedError可能會更好。)
關於斷言的意見有很多,因為它能確保代碼的正確性。如果你確定代碼是正確的,那麼就沒有用斷言的必要了,因為他們從來不會運行失敗,你可以直接移除這些斷言。如果你確定檢查會失敗,那麼如果你不用斷言,代碼就會通過編譯並忽略你的檢查。
在以上兩種情況下會很有意思,當你比較肯定代碼但是不是絕對肯定時。可能你會錯過一些非常古怪的情況。在這個情況下,額外的運行時檢查能幫你確保任何錯誤都會盡早地被捕捉到。
另一個好的使用斷言的方式是檢查程序的不變數。一個不變數是一些你需要依賴它為真的情況,除非一個bug導致它為假。如果有bug,最好能夠盡早發現,所以我們為它進行一個測試,但是又不想減慢代碼運行速度。所以就用斷言,因為它能在開發時打開,在產品階段關閉。
一個非變數的例子可能是,如果你的函數希望在它開始時有資料庫的連接,並且承諾在它返回的時候仍然保持連接,這就是函數的不變數:
Python
def some_function(arg):
assert not DB.closed()
... # code goes here
assert not DB.closed()
return result
斷言本身就是很好的注釋,勝過你直接寫注釋:
# when we reach here, we know that n > 2
你可以通過添加斷言來確保它:
assert n > 2
斷言也是一種防禦型編程。你不是讓你的代碼防禦現在的錯誤,而是防止在代碼修改後引發的錯誤。理想情況下,單元測試可以完成這樣的工作,可是需要面
對的現實是,它們通常是沒有完成的。人們可能在提交代碼前會忘了運行測試代碼。有一個內部檢查是另一個阻擋錯誤的防線,尤其是那些不明顯的錯誤,卻導致了
代碼出問題並且返回錯誤的結果。
加入你有一些if…elif 的語句塊,你知道在這之前一些需要有一些值:
# target is expected to be one of x, y, or z, and nothing else.
if target == x:
run_x_code()
elif target == y:
run_y_code()
else:
run_z_code()
假設代碼現在是完全正確的。但它會一直是正確的嗎?依賴的修改,代碼的修改。如果依賴修改成 target = w
會發生什麼,會關繫到run_w_code函數嗎?如果我們改變了代碼,但沒有修改這里的代碼,可能會導致錯誤的調用 run_z_code
函數並引發錯誤。用防禦型的方法來寫代碼會很好,它能讓代碼運行正確,或者立馬執行錯誤,即使你在未來對它進行了修改。
在代碼開頭的注釋很好的一步,但是人們經常懶得讀或者更新注釋。一旦發生這種情況,注釋會變得沒用。但有了斷言,我可以同時對代碼塊的假設書寫文檔,並且在它們違反的時候觸發一個干凈的錯誤
assert target in (x, y, z)
if target == x:
run_x_code()
elif target == y:
run_y_code()
else:
assert target == z
run_z_code()
這樣,斷言是一種防禦型編程,同時也是一種文檔。我想到一個更好的方案:
if target == x:
run_x_code()
elif target == y:
run_y_code()
elif target == z:
run_z_code()
else:
# This can never happen. But just in case it does...
raise RuntimeError("an unexpected error occurred")
按約定進行設計是斷言的另一個好的用途。我們想像函數與調用者之間有個約定,比如下面的:
「如果你傳給我一個非空字元串,我保證傳會字元串的第一個字母並將其大寫。」
如果約定被函數或調用這破壞,代碼就會出問題。我們說函數有一些前置條件和後置條件,所以函數就會這么寫:
def first_upper(astring):
assert isinstance(astring, str) and len(astring) > 0
result = astring[0].upper()
assert isinstance(result, str) and len(result) == 1
assert result == result.upper()
return result
按約定設計的目標是為了正確的編程,前置條件和後置條件是需要保持的。這是斷言的典型應用場景,因為一旦我們發布了沒有問題的代碼到產品中,程序會是正確的,並且我們能安全的移除檢查。
下面是建議的不要用斷言的場景:
不要用它測試用戶提供的數據
不要用斷言來檢查你覺得在你的程序的常規使用時會出錯的地方。斷言是用來檢查非常罕見的問題。你的用戶不應該看到任何斷言錯誤,如果他們看到了,這是一個bug,修復它。
有的情況下,不用斷言是因為它比精確的檢查要短,它不應該是懶碼農的偷懶方式。
不要用它來檢查對公共庫的輸入參數,因為它不能控制調用者,所以不能保證調用者會不會打破雙方的約定。
不要為你覺得可以恢復的錯誤用斷言。換句話說,不用改在產品代碼里捕捉到斷言錯誤。
不要用太多斷言以至於讓代碼很晦澀。
『陸』 限流器有什麼用限流器的原理是什麼
限流器是一種限制某種操作在一定時間內的執行次數(例如每秒鍾5次)或者執行量(例如每秒鍾1G大小的數據)的機制。
限流器是一種防禦性的編程實現方式,在大數據量高並發訪問時,經常會出現服務或介面面對暴漲的請求而不可用的情況,甚至引發連鎖反映導致整個系統崩潰。此時你需要使用的技術手段之一就是限流,當請求達到一定的並發數或速率,就進行等待、排隊、降級、拒絕服務等。
限流器優點
動作速度快,反應時間小於20ms(在一個電力周波內),可在電力系統故障時自動觸發;能將短路電流減少一半以上。
故障線路被斷路器開斷後,能快速自動復位並在幾秒之內多次動作,以配合重合閘,正常運行時,功耗應接近於零,最大不能高於輸送功率的0.25%,可靠性應高於與其同時運行的斷路器。