Total Pageviews

2016/09/02

[閱讀筆記] 97 Things Every Programmer Should Know (1/2)


  1. 最新式的電腦,只不過是加速加重了人與人之間一直以來的問題,最終,溝通者要面對的,依然在於如何表達出自己要說的
  2. 技術債 (technical debt) 就像貸款,你可以為了時程決定「先快速做完」,而不是「做好」,你會在短期內得利,但在債務還清之前都必須支付利息。滿足一時的程式碼,會使新增功能與重構變得困難。時間越久,傷害越大。事實上,往往都是當事情糟到你不得不修正時,你才會真的回頭去解決當初的問題,但是這時候程式碼已經變得很難修正。
  3. 記得要儘快償還技術債 (technical debt),否則會因為草率的行為而嚐到惡果。
  4. 我們常認為別人的想法和我們一樣,但事實上不是。心理學將此稱為共識偏誤(false consensus bias)。請直接去問使用者會怎麼操作,你不算是使用者
  5. 使用者傾向於只要有能用的方法就好,他們一旦找到一種可行的方法之後,就會一直用,不論這個方法是多麼迂迴。因此,最好準備一條明顯的道路,而不是給兩三種道路供選擇
  6. 你也會發現使用者所描述的,和他們實際做的往往有段落差。因此最好的方法是直接觀察使用者,花幾個小時觀察,比起花一整天想使用者到底要什麼,反而可以得到更多資訊
  7. coding standard 應該是靈活不死板的,隨著專案演進,需求改變,一些當初看似聰明的想法,在幾個月後已經不見得聰明了
  8. 柏拉圖曾說:風格之美、和諧、優美與節奏取決於簡約。美麗的程式的最低限度是簡單的程式碼。不只職責簡單,與系統的其他部分也要保持簡單的關係。簡單、乾淨、可測試的程式碼,可以提高可讀性、可維護性、開發效率。美,就在簡單中誕生,在簡單之中發現
  9. 在你重構前,你要思考:
    1. 從現有的程式碼與其對應的測試開始,從當前系統的錯誤中學到教訓
    2. 避免全部重寫的誘惑,重寫代表要放棄經年累月的測試
    3. 多次漸進式的修改會優於一次大量的修改
    4. 每次development iteration結束後,要確保現有的測試都必須通過
    5. 程式碼的結構與風格不符合你的喜好,不是重構的正確理由
    6. 新技術的出現不足以成為重構的理由
    7. 每個人都是會犯錯的,重構不保證新的程式碼一定比較好
  10. 童子軍有一條規則:『每次離開營區時,都要讓它比剛到的時候更乾淨』。也就是說,如果你發現環境髒亂,不管是誰造成的,你都要把它打掃乾淨,留給下一批露營者有更好的環境。相同的,你不必在 check in 前讓每個模組盡善盡美,只要讓它比當初 check out 的時候好一點點就可以,如改善了變數名稱、將一個過長的function拆成兩個小的function等。軟體開發團隊要互相幫助,互相清理程式碼。成員門遵循童子軍規則,不只是為了自己,而是因為這對每一個人都好。
  11. Debug 時,回想福爾摩斯的建議:「除去不可能的,剩下的即使不尋常,那也是真相。」
  12. 面對multi-thread 的系統,簡潔的設計非常重要,簡潔代表容易debug
  13. 程式碼的排版很重要,有研究指出,我們花在瀏覽和閱讀程式碼,找出哪裡該修改的時間,比實際修改的時間多更多。所以程式排版有三個優化措施:易於瀏覽、清楚的排版、緊湊的格式
  14. code review 不只是找出並修正程式錯誤,更重要的是分享知識,並建立普遍的coding方針。code review 的重點是在 review 的過程當中去學習、了解這些程式碼
  15. 採用表達力佳(但相對較短)的名稱來命名物件、型別與函式,讓程式碼本身具備如文件的說明能力
  16. 註解應該要說出程式碼所沒有表達、也無法表達的事情。如果程式自己可以說清楚的事情,你就不需要為它加上註解
  17. 外科醫生知道必須要切出傷口才能進行手術,也知道傷口是暫時的,會痊癒的。害怕改變程式碼導致癱瘓,可能正是造成系統落入癱瘓的起點。投入時間 refactor,對於專案的整個生命週期可以帶來數倍的價值,也因為有了處理病態系統的經驗,也成為了解系統應當如何運作的專家
  18. 無論你認為你的程式碼有多麼不可能出現錯誤,你也應該每一次都檢查並處理,即使你現在不做,你也不會省下時間,你未來還是要還債,而且累積越久,要還的債更多,未來會連本帶利要你償還
  19. 如果你忽視一項錯誤,選擇視而不見,假裝沒這件事情,你其實在承擔巨大的風險,你也無法預料這個巨大的風險會在何時向你反撲。
  20. 放著錯誤不管會造成
    1. 脆弱的程式碼且難以debug
    2. 不安全的程式碼
    3. 糟糕的結構
  21. 沒有開發經驗的專案經理會認為程式設計師做的事情很簡單,而沒有管理經驗的程式設計師,也認為專案經理做的事情很簡單
  22. 軟體的構成實體,應當對於擴充保持開放,但對修改保持封閉
  23. DRY ( Don’t Repeat Yourself ) 的原則,是指開發人員要學會便是重複的程式碼,也懂得利用更好的實踐和更適當的抽象化來消除重複,比起只知道用不必要的重複來污染程式的人,可以產出更乾淨的程式碼。DRY 原則,位開發人員提供基本指引,幫助開發人員所產出的程式變得更簡單、更容易維護且品質更好
  24. 假設有個物件,其典型的特色是需要先完成幾件事情才能開始使用,此時就可以使用abstract factory pattern or factory method pattern 來實現。如果某個物件有多種可變的行為,那麼這些行為可以用strategy pattern 來實作並放進物件,不該用一個巨大的 if-else 結構
  25. 當一個class 的containment (封裝)被破壞,常會看到其method 出現大量的if-then-else的結構,這樣的method 很容易崩壞且幾乎無法維護


2016/09/01

[閱讀筆記] Security Analysis (Part 3)


  1. 債券的價格與收益,不是經過預期風險的數學計算所決定,其由市場受歡迎度所決定。市場歡迎度反應投資人對此投資標的的風險認定,還有其他因素如社會大眾對此標的的熟悉度以及交易容易度
  2. 收益與風險之間的關係無法做精確的數學計算,這不像保險業中的年齡與死亡率之間的關係,有明確的定義。投資的損失不是某個時間點,而是一段時間區間
  3. 大型的公司並不保證低風險與健全的財務,當一家公司的資產債務比不成比例的話, 即便是大型的公司,體質一樣脆弱
  4. 優先股是股票和債券的混血產品,但表現比較像是債券。持有優先股的投資人通常可以領取固定的股利(不論公司盈虧)、若公司破產戶清算時(在普通股之前,債權人之後)優先請求發行公司的收益和資產
  5. 優先股的特色有:優先股如債券一樣,如果通貨膨脹率和利率上升,優先股的價值會下滑,如果通貨膨脹率和利率下降,優先股的價值會上揚;優先股投資人不能參與公司的成長,普通股股東則能獲得的資本利得;優先股不像債券那麼安全,因為股利可能逾期未發放
  6. 非累積優先股的持有者要注意,公司若該年度的營運不佳或即便是營運良好,公司可以不按照規定的股利分配給股東。累積優先股的股東也不能要求公司在以後年度中予以補發。一般來講,對投資者來說,累積優先股比非累積優先股具有更大的優越性
  7. 一般公司會發行優先股有幾個情況:1. 籌資清償債務。2. 籌資度過難關。3. 增加公司資產。公司通常只發行一種普通股,卻可發行好幾種優先股。第一優先配發股利的股票,稱作第一優先股或A級優先股,接下來的優先股稱為第二優先股或B級優先股,以下類推
  8. 投資人不要看到"保證收益"的字眼就被沖昏了頭,似乎其提供了安全、無風險的保障,但是實際上,還是要端視發行者的財務狀況。若保證者的財務狀況不佳,其保證是完全沒有保障的
  9. 1931年時,不動產市場潰堤,不動產債券持有人總算可以認清,不動產債券的抵押品只是一個名字,其價值完全仰賴其抵押品在當時潰堤後所剩餘的價值
  10. 不要因B公司是某強大母公司的子公司,而買此子公司的債券,把合約看清楚,當B公司違約時,母公司是否有需要做擔保,若沒有時,那你要考慮清楚
  11. 投資人應該定期檢視手上的投資標的,不要認為其永遠安全無虞。當發現投資標的有問題時,應該將其轉換到另外一個較好的投資標的
  12. 如遇到1921-1922、1937-38那種大蕭條的時候,不管是什麼樣的投資組合,一定都很難全身而退、不受影響。不過這時也是一個好時機,針對自己的投資組合做好體檢,並可考慮剃除體質不良的投資標的,並轉換到較好的標的
  13. 一般大眾常會尋求銀行的專員提供理財建議,但是其是否正確,有待懷疑。別以為靠理專的建議就可以省下你研究的時間,想要投資就要自己分析、挖掘問題
  14. 投資銀行(Investment Banking Houses)的建議並不可靠,因為他們不會是一個公正、客觀的理財顧問,因為他們自己就在販售基金,當然是優先推薦自己家的基金,故別把販售基金的公司的建議當作你的主要資訊來源
  15. 如果一直想著想抓住市場鐘擺,永遠想要買在低點、賣在高點,那你是在投機不是在投資
  16. 在牛市的時候,跟著人群走很容易,但是判斷哪些是值得買、沒有超過其真實價值的標的,卻是很困難的
  17. 人性很習慣的跟著人群走,尤其在dot-com boom的時候,當每個人都在快速致富,你不太容易能保持冷靜,當個旁觀者
  18. 清算價值(Liquidation Value)是指公司撤銷或解散時,資產經過清算後,每一股份所代表的實際價值。在理論上,清算價值等於清算時的帳面價值,但由於公司的大多數資產只以低價售出,再扣除清算費用後,清算價值往往小於帳面價值
  19. 當公司的股價低於清算價值(Liquidation Value),要麻就是股價太低,不然就是公司即將解散
  20. 「價格」與「價值」的缺口,就代表投資的安全保障或邊際,如以40美分的價格買進1美元的紙鈔
  21. 當一家了不起的企業,遭遇一個巨大、嚴重,但可解決的問題的衝擊時,這時就是一個絕佳的投資機會
  22. Buffett 會去找一些宣告破產的公司進行評估,若其債務收益率大於目前的權益,他會大舉買進,並取得公司的控制權。以MCI公司為例,若清算其資產只有40億,但是若讓他持續經營,可以產生120~150億的收入,讓MCI重整會比清算來得有利
  23. Graham and Dodd 認為,投資人不應該去購買低評等債券與優先股,應該把注意力放在普通股
  24. 股票市場就像一台voting machine,會產生這樣的結果,一部分是偏見,一部分是情緒,而不是一台精確的、公正的weighing machine  
  25. 市場的價格常常會與其真實價格不一致,但是市場有與生俱來的能力來修正此差異
  26. 當一家公司的股票突然停止發放股利時,你應該要考慮去賣掉它。相同的,當一檔債券突然停止配息,你也應該考慮要賣掉它。
  27. Wall Street 裡面沒有 bull,也沒有bear,只有shark,他會持續地尋找食物,把那些資產錯誤配置的投資人的金錢吃光光
  28. 多頭市場 (bull market) 總是在悲觀中誕生,在懷疑中成長,在樂觀中成熟,在陶醉中死亡
  29. 證券的價格與收益無法透過任何精確的數學計算運算出來,其端視本身受歡迎的程度來決定
  30. Graham與Dodd建議不要購買高收益債券,因為當遭遇危機,經過崩壞後,大部份都無法復原
  31. 人性傾向貪婪、恐懼、猜疑與壓力,喜歡跟著人群走,你要有獨立思考能力,抵抗人性弱點,不要害怕與眾不同
  32. A convertible bond should not be converted by the investor. It should be either held or sold.
  33. 債券的收益來自當初承諾的配息率;股票的收益來自企業經營績效所配發的股利
  34. 股票投資的基本問題是:如何評估一間企業的價值。在1930年代,那時後的企業以煤礦、鐵路、公共事業為主,所以以評估有形資產即可;但目前是以服務業為主的時代,諸如品牌、軟體等,都屬於無形資產,故比以前更難評估
  35. 自由現金流(free cash flow)表示的是公司可以自由支配的現金。如果自由現金流豐富,則公司可以償還債務、開發新產品、回購股票、增加股息支付

2016/08/31

[Travel] Kyoto (京都)

清水寺
DSC09960

DSC09988



う桶や「う」
DSC00066



小山園
DSC00008


嵯峨野観光鉄道トロッコ列車 
DSC00093

嵯峨野嵐山竹林
DSC00173

鴨川
DSC00212

曼殊院門跡
DSC00260

一乘寺惠文社
DSC00267
永観堂
DSC00311

DSC00313

DSC00330

DSC00379

2016/08/30

[Travel] Osaka (大阪)

Osaka Castle 大阪城
DSC09856

DSC09874



大阪歷史博物館
DSC09884

DSC09916

DSC09898



大阪 道頓崛
DSC09946

DSC09952

DSC09953

2016/08/09

[Git] Frequently Used Command

1. how to show file change list?
 
 
 
 
 
 
 
 
 
albert@albert-PC MINGW64 /d/git/issue-webapp (master)
$ git status
On branch master
Your branch is behind 'origin/master' by 6 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   src/app/journal/journal.list.component.ts
        modified:   src/app/journal/journal.list.html


2. How to create a branch from master? (given your branch name after -b)

albert@albert-PC MINGW64 /d/git/issue-webapp (master)
$ git checkout -b fix#165468
       src/app/journal/journal.list.component.ts
       src/app/journal/journal.list.html
Switched to a new branch 'fix#165468'


3. How to discard changes? (assign the file path after git checkout)

albert@albert-PC MINGW64 /d/git/issue-webapp (fix#165468)
$ git checkout  src/app/journal/journal.list.component.ts


4. How to change from my local branch to master?


albert@albert-PC MINGW64 /d/git/issue-webapp (fix#165468)
$ git checkout master


5. How to add all changed files and commit to local? (remember to add comments as you do commit)
albert@albert-PC MINGW64 /d/git/issue-webapp (fix#165468)
$ git add --all

albert@albert-PC MINGW64 /d/git/issue-webapp (fix#165468)
$ git commit -m 'fix #165468'
[fix#165468 dbec729] fix #165468
 2 files changed, 25 insertions(+), 5 deletions(-)


6. How to push commits made on your local branch to a remote repository? (assign your branch name after origin)
 albert@albert-PC MINGW64 /d/git/issue-webapp (fix#165468)
$ git push origin fix#165468


7. How to synchronize my local repository with remote repository?
albert@albert-PC MINGW64 /d/git/issue-webapp (master)
$ git pull --rebase upstream master
From https://github.com/ChunghwaTelecom/issue-webapp
 * branch            master     -> FETCH_HEAD
 * [new branch]      master     -> upstream/master
First, rewinding head to replay your work on top of it...
Fast-forwarded master to b393bd26111dbe2335bf1fb5d9cf3cb110c278ca.


8. How to list values of all configuration? 

albert@albert-PC MINGW64 /d/git/issue-webapp (master)
$ git config --list
core.symlinks=false
core.autocrlf=true
core.fscache=true
color.diff=auto
color.status=auto
color.branch=auto
color.interactive=true
help.format=html
http.sslcainfo=C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
sendemail.smtpserver=/bin/msmtp.exe
diff.astextplain.textconv=astextplain
rebase.autosquash=true


9. 
How to configure remote.upstream.url to https://gitlab.com/ChunghwaTelecom/rakr-chrome-extension.git ?
albert@albert-PC MINGW64 /d/git/issue-webapp (master)
$ git config remote.upstream.url https://gitlab.com/ChunghwaTelecom/rakr-chrome-extension.git


10. Check commit history 
git log --pretty=oneline -10




2016/08/08

[API] [JAVA] How to send SMS via 三竹簡訊

Requirement
Our web application has SMS (short message service) requirement. Our customer asks to send short message by 三竹簡訊.

Based on its documentation, it provide 8 ways to send short message, and we choose approach 4th.


Here has an diagram to describe 三竹資訊's architecture:


We will use Http Get method to send user name, password, phone number and short message to SmGateway:


And it will return response to your source code:


The status code and its description are as follows:



How-to
The process looks like:


Here has the sample code.
This enumeration contains the status code and its description:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package albert.practice.sms;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
public enum SmsStatusCodeEnum {

    STATUS_STAR("*", "系統發生錯誤,請聯絡三竹資訊窗口人員"),
    STATUS_a("a", "簡訊發送功能暫時停止服務,請稍候再試"),
    STATUS_b("b", "簡訊發送功能暫時停止服務,請稍候再試"),
    STATUS_c("c", "請輸入帳號"),
    STATUS_d("d", "請輸入密碼"),
    STATUS_e("e", "帳號、密碼錯誤"),
    STATUS_f("f", "帳號已過期"),
    STATUS_h("h", "帳號已被停用"),
    STATUS_k("k", "無效的連線位址"),
    STATUS_m("m", "必須變更密碼,在變更密碼前,無法使用簡訊發送服務"),
    STATUS_n("n", "密碼已逾期,在變更密碼前,將無法使用簡訊發送服務"),
    STATUS_p("p", "沒有權限使用外部Http程式"),
    STATUS_r("r", "系統暫停服務,請稍後再試"),
    STATUS_s("s", "帳務處理失敗,無法發送簡訊"),
    STATUS_t("t", "簡訊已過期"),
    STATUS_u("u", "簡訊內容不得為空白"),
    STATUS_v("v", "無效的手機號碼"),
    STATUS_0("0", "預約傳送中"),
    STATUS_1("1", "已送達業者"),
    STATUS_2("2", "已送達業者"),
    STATUS_3("3", "已送達業者"),
    STATUS_4("4", "已送達手機"),
    STATUS_5("5", "內容有錯誤"),
    STATUS_6("6", "門號有錯誤"),
    STATUS_7("7", "簡訊已停用"),
    STATUS_8("8", "逾時無送達"),
    STATUS_9("9", "預約已取消");

    @Getter
    private String code;

    @Getter
    private String description;

}

This utility class is used to send short message:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package albert.practice.sms;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang.StringUtils;

import com.google.common.collect.Lists;

/**
 * username = 使用者帳號。SmGateway資料庫表格SMUser中需有此使用者,且狀態為啟用。 <br>
 * password = 使用者密碼 <br>
 * dstaddr = 受訊方手機號碼 <br>
 * DestName = 收訊人名稱。若其他系統需要與簡訊資料進行系統整合,此欄位可填入來源系統所產生的Key值,以對應回來源資料庫 <br>
 * dlvtime = 簡訊預約時間。格式為YYYY-MM-DD HH:NN:SS或YYYYMMDDHHNNSS,或是整數值代表幾秒後傳送。<br>
 * vldtime = 簡訊有效期限。格式為YYYY-MM-DD HH:NN:SS或YYYYMMDDHHNNSS,或是整數值代表傳送後幾秒後內有效。 <br>
 * smbody = 簡訊內容。必須為BIG-5編碼,長度70個中文字或是160個英數字。若有換行的需求,請填入ASCII Code 6代表換行。 <br>
 */
@Slf4j
public class SmsUtils {

    private final static String USER_AGENT = "Mozilla/5.0";

    private final String SMS_URL = "http://xxx/SmSendGet.asp?";
    private final String USER_NAME = "Test001";
    private final String PASSWORD = "TestPwd";

    public static void main(String[] args) throws Exception {
        String phone = "09";
        String smBody = "您的驗證碼為○○○○○○,請於收到簡訊10分鐘內完成驗證。";
        new SmsUtils().sendSms(phone, smBody);
    }

    public void sendSms(String phoneNumber, String body) throws Exception {
        body = URLEncoder.encode(body, "Big5");
        sendGet(phoneNumber, body);
    }

    private void sendGet(String phoneNumber, String body) throws IOException {

        StringBuilder url = new StringBuilder();
        url.append(SMS_URL);
        url.append("username=").append(USER_NAME);
        url.append("&password=").append(PASSWORD);
        url.append("&dstaddr=").append(phoneNumber);
        url.append("&smbody=").append(body);

        URL obj = null;
        BufferedReader bufferedReader = null;
        try {
            obj = new URL(url.toString());
            HttpURLConnection con = (HttpURLConnection) obj.openConnection();

            // optional default is GET
            con.setRequestMethod("GET");

            // add request header
            con.setRequestProperty("User-Agent", USER_AGENT);

            int responseCode = con.getResponseCode();

            log.debug("\nSending 'GET' request to URL : " + url);
            log.debug("Response Code : " + responseCode);

            bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String inputLine = "";
            StringBuffer response = new StringBuffer();

            while ((inputLine = bufferedReader.readLine()) != null) {
                response.append(inputLine);
            }

            // print result
            String responseStr = response.toString();
            log.debug(responseStr);

            int from = responseStr.indexOf("statuscode=");
            int to = responseStr.indexOf("AccountPoint=");
            String tmpStr = StringUtils.substring(responseStr, from, to);

            // 取得status code
            String statusCode = StringUtils.substring(tmpStr, tmpStr.length() - 1, tmpStr.length());
            log.info("statusCode = " + statusCode);

            // 將status code 轉成 中文訊息
            String executionResult = getDescription(statusCode);
            log.info("executionResult = " + executionResult);

            List<String> successList = Lists.newArrayList("0", "1", "2", "3", "4"); // 成功的SATUSCODE
            if (!successList.contains(statusCode)) {
                throw new RuntimeException(executionResult);
            }

        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (bufferedReader != null) {
                bufferedReader.close();
            }
        }
    }

    /**
     * 將status code 轉成 中文訊息
     * 
     * @param statusCode
     * @return 中文訊息
     */
    private String getDescription(String statusCode) {
        String description = "";
        for (SmsStatusCodeEnum smsStatusCode : SmsStatusCodeEnum.values()) {
            if (smsStatusCode.getCode().equals(statusCode)) {
                description = smsStatusCode.getDescription();
            }
        }
        return description;
    }
}