# Topic 22 - 24 ###### tags: `The Pragmatic Programmer` ## Topic 22 Engineering Daybooks :::spoiler story Dave once worked for a small computer manufacturer, which meant working alongside electronic and sometimes mechanical engineers. Many of them walked around with a paper notebook, normally with a pen stuffed down the spine. Every now and then when we were talking, they’d pop the notebook open and scribble something. Eventually Dave asked the obvious question. It turned out that they’d been trained to keep an engineering daybook, ==a kind of journal in which they recorded what they did, things they’d learned, sketches of ideas, readings from meters: basically anything to do with their work.== When the notebook became full, they’d write the date range on the spine, then stick it on the shelf next to previous daybooks. There may have been a gentle competition going on for whose set of books took the most shelf space. ::: use daybooks to take notes in meetings, to jot down what we’re working on, to note variable values when debugging, to leave reminders where we put things, to record wild ideas, and sometimes just to doodle. The daybook has three main benefits: - It is more reliable than memory. - It gives you a place to store ideas that aren’t immediately relevant to the task at hand. - It acts as a kind of rubber duck (described here). > 試著用紙筆筆記本,寫工程日誌一個月! # chapter 4 Pragmatic Paranoia 務實的偏執 :::warning **You Can’t Write Perfect Software** Accept it as an axiom of life. Embrace it. Celebrate it. Because perfect software doesn’t exist. ::: - Everyone knows that they personally are the only good driver on Earth.(其他人都是三寶,都不好遵守規則XD),So we drive defensively(防禦性駕駛). - 撰寫程式碼也是如此。we are taught to code defensively. assertions( to detect bad data), check for consistency(put constraints on database columns) and ==feel pretty good about ourselves.== But Pragmatic Programmers take this a step further.... :::info They don’t trust themselves, either. ::: ## Topic 23 Design by Contract 合約式設計 One of the best solutions for ensuring plain dealing is the contract. ### DBC(Design by Contract) Bertrand Meyer (Object-Oriented Software Construction [Mey97]) developed the concept of Design by Contract for the language Eiffel. documenting the rights and responsibilities of software modules to ensure program correctness. > What is a correct program? One that does no more and no less than it claims to do. :::info Documenting and verifying that claim is the heart of DBC。 ::: 1. Preconditions 前置條件 What must be true in order for the routine to be called; 當前置條件被違反,他就不應該被呼叫。 2. Postconditions 後置條件 函式保證要做的事。 What the routine is guaranteed to do; the state of the world when the routine is done. (會得出結論,infinite loops aren’t allowed.) 3. Class invariants 類別不變量 類別必須確保不變量條件始終成立 A class ensures that this condition is always true from the perspective of a caller. 當函式退出並控制傳給呼叫者時,不變量必須成立。 The contract between a routine and any potential caller can thus be read as: :::warning If all the routine’s preconditions are met by the caller, the routine shall guarantee that all postconditions and invariants will be true when it completes. 如果呼叫者滿足了前置條件,則函式應該在完成時保證所有後置條件與不變量都成立 ::: 如果有任何一方未能履行合約,就會呼叫補救措施。 (exception is raised, or the program terminates.) 不管怎樣,不能履行合約都是一個錯誤,不應該發生~ 有些語言更能支援這個概念,例如 ### Clojure:範例:存款函式 :pre => 前置條件 1. amount is greater than zero 2. account is open and valid :post => 後置條件 1. the function guarantees that the new transaction can be found among the transactions for this account. ```=Clojure (defn accept-deposit [account-id amount] { :pre [ (> amount 0.00) (account-open? account-id) ] :post [ (contains? (account-transactions account-id) %) ] } "Accept a deposit and return the new transaction id" ;; Some other processing goes here... ;; Return the newly created transaction: (create-transaction account-id :deposit amount)) ``` if there’s a bug in the program and you somehow passed in a negative amount for the deposit, you’ll get a runtime exception: >Exception in thread "main"... Caused by: java.lang.AssertionError: Assert failed: (> amount 0.0) >Exception in thread "main"... Caused by: java.lang.AssertionError: Assert failed: (account-open? account- id) ### Elixir:guard clause ```=Elixir defmodule Deposits do def accept_deposit(account_id, amount) when (amount > 100000) do # Call the manager! end def accept_deposit(account_id, amount) when (amount > 10000) do # Extra Federal requirements for reporting # Some processing... end def accept_deposit(account_id, amount) when (amount > 0) do # Some processing... end end ``` 若不符合將丟出例外: >** (FunctionClauseError) no function clause matching in Deposits.accept_deposit/2 :::info This is a better approach than simply checking your inputs; in this case, you simply can not call this function if your arguments are out of range. ::: ### Design with Contracts #### Class Invariants and Functional Languages What this idea really refers to is state. 其實就是狀態 state ![](https://i.imgur.com/dC4zFLg.png) #### DBC and Test-Driven Development > is DBC needed in a world where developers practice unit testing, test- driven development (TDD), property-based testing, or defensive programming? **YES,DBC and testing are different approaches** They both have value and both have uses in different situations. DBC offers several advantages over specific testing approaches: 1. DBC doesn’t require any setup or mocking 2. DBC defines the parameters for success or failure in all cases, whereas testing can only target one specific case at a time 3. TDD and other testing happens only at “test time” within the build cycle. But DBC and assertions are forever: during design, development, deployment, and maintenance 4. TDD does not focus on checking internal invariants within the code under test,it’s more black-box style to check the public interface. 5. DBC is more efficient (and DRY-er) than defensive programming, where everyone has to validate data in case no one else does. TDD is a great technique, but as with many techniques, it might invite you to concentrate on the “happy path,” and not the real world full of bad data, bad actors, bad versions, and bad specifications. Is it 🤔❓ #### IMPLEMENTING DBC Simply enumerating what the input domain range is, what the boundary conditions are, and what the routine promises to deliver—or, more importantly, what it doesn’t promise to deliver. In languages that do not support DBC in the code, this might be as far as you can go—and that’s not too bad. ==DBC is, after all, a design technique. Even without automatic checking, you can put the contract in the code as comments or in the unit tests and still get a very real benefit. 合約式設計是一種設計技術== #### Assertions(斷言) 檢查一些不應該發生的事情,當你覺得“當然這是不可能發生的”,加入程式碼來檢查他! https://caterpillar.gitbooks.io/javase6tutorial/content/c10_5.html #### DBC AND CRASHING EARLY 早期崩壞 By using an assert or DBC mechanism to validate the preconditions, postconditions, and invariants, you can crash early and report more accurate information about the problem. exmaple: 計算平方根的方法 * 前置條件,input 限制為正數 In languages that support DBC, if you pass sqrt a negative parameter, you’ll get an informative error such as `sqrt_arg_must_be_positive,` along with a stack trace. 那在其他語言呢?將複數傳給計算平方根的方法,回傳NaN, ...後續根據回傳做更多操作.. * 傳遞正確資料是誰的責任?是呼叫者還是被呼叫的函示? :::spoiler ANSWE 當一種語言把檢查前置條件實作為一部份, 答案是兩者都不該負責, 因為在呼叫此函式之後,但在進入韓式之前,前置條件會在背景被測試。 針對有支援的語言=>如果要執行任何的手動的參數檢查,必須在 呼叫者端做,因為被呼叫函式本身永遠不會看到違反其前置條件的參數 針對沒有支援的語言=> 需要在 被呼叫的函式 加上一組前綴/後綴 來做 ssertion 檢查 ::: 有一支函式,從console 讀取一個數字, 計算平方根(呼叫sqrt),印出結果。sqrt 有一個前置條件,參數不能是負數。 假設使用者在console 輸入了一個負數,呼叫端程式碼要確保這個負數不會傳給sqrt。 sqrt 前置條件表達了可接受的值,將正確性的負擔轉移到呼叫程式,這樣可以放心的設計 sqrt 函式,因為他拿到的輸入將會在範圍內。 #### SEMANTIC INVARIANTS 語意不變量,表達不可違背的需求,a kind of “philosophical contract.''哲學合約 * invariants—it must be central to the very meaning of a thing, and not subject to the whims of policy。一個東西的重要意義,請不會受到政策的影響。 Err in favor of the consumer. This is a clear, concise, unambiguous statement that’s applicable in many different areas of the system. It is our contract with all users of the system, our guarantee of behavior. #### CHALLENGES Points to ponder: If DBC is so powerful, why isn’t it used more widely? Is it hard to come up with the contract? Does it make you think about issues you’d rather ignore for now? Does it force you to THINK!? Clearly, this is a dangerous tool! #### 14 * Design an interface to a kitchen blender. It will eventually be a web-based, IoT-enabled blender, but for now we just need the interface to control it. It has ten speed settings (0 means off). You can’t operate it empty, and you can change the speed only one unit at a time (that is, from 0 to 1, and from 1 to 2, not from 0 to 2). ``` Here are the methods. Add appropriate pre- and postconditions and an invariant. int getSpeed() void setSpeed(int x) boolean isFull() void fill() void empty() ``` :::spoiler answer ```=java //1. You can’t operate it empty //2. change the speed only one unit at a time // 類別的不變數: @invaraint getSpeed() > 0 implies isFull() // 空無一物的時候不運轉 @invaraint getSpeed() >= 0 && getSpeed() < 10 // 轉速段位檢查 // 前置 後置 條件 @pre Math.abs(getSpeed()-x)<=1 // 一次只能切換一段 @pre x >= 0 && x < 10 // 轉速段位檢查 @post getSpeed() == x // 施行要求的轉速段位 public void setSpeed(final int x) @pre !isFull() //不能填充兩次東西 @post isFull // 確保有填充東西 void fill() @pre isFull() // 不能清空兩次 @post !isFull() // 確保已執行清空 void empty() ``` ::: #### 15 * How many numbers are in the series 0, 5, 10, 15, ..., 100? :::spoiler answer 21 ::: ## Topic 24 Dead Programs Tell No Lies Have you noticed that sometimes other people can detect that things aren’t well with you before you’re aware of the problem yourself? It’s the same with other people’s code. we want to know when the “impossible” has happened. **It’s easy to fall into the “it can’t happen” mentality.** * All errors give you information. :::info Pragmatic Programmers tell themselves that if there is an error, something very, very bad has happened. Don’t forget to Read the Damn Error Message. ::: ### CATCH AND RELEASE IS FOR FISH Some developers feel that is it good style to catch or rescue all exceptions: ``` try do add_score_to_board(score); rescue InvalidScore Logger.error("Can't add invalid score. Exiting"); raise rescue BoardServerDown Logger.error("Can't add score: board is down. Exiting"); raise rescue StaleTransaction Logger.error("Can't add score: stale transaction. Exiting"); raise end ``` Pragmatic Programmers would write this: ``` add_score_to_board(score); ``` 1. the application code isn’t eclipsed by the error handling. 2. the code is less coupled Tip 38: Crash Early ### CRASH, DON’T TRASH One of the benefits of detecting problems as soon as you can is that you can crash earlier. The Erlang and Elixir languages embrace this philosophy. > Joe Armstrong, is often quoted as saying, “Defensive programming is a waste of time. Let it crash!” #### In these environments, programs are designed to fail, but that failure is managed with supervisors. :::spoiler supervisors? 主管負責啟動、停止和監視其子進程。 當一個進程崩潰是,它能夠快速重啟該進程,並根據不同的策略做出不同的行為。 https://blog.csdn.net/fbher/article/details/93209407 https://www.erlang.org/doc/design_principles/sup_princ.html ::: #### In other environments it may be inappropriate simply to exit a running program. 有可能有已經要求使用但未被釋放的資源...互動等等 <br> However, the basic principle stays the same—when your code discovers that something that was supposed to be impossible just happened, your program is no longer viable. ==Anything it does from this point forward becomes suspect, so terminate it as soon as possible.== :::info A dead program normally does a lot less damage than a crippled one. :::