之前完成的程式,用 Class Diagram 隨便畫一下,大概的關係圖如下:
由於 WatchDir 中,為了使用 textArea 來顯示訊息,所以把 textArea 當作參數傳給 WatchDir。此外,textArea 使用String.format處理產生的訊息。
這樣的寫法,其實變成日後要加新的「訊息處理者」,如 MessageDialog 來產生 alert 對話視窗時,變成要再加參數傳給 WatchDir。而且使用的 ? 方法,也要另外再寫 String.format 來處理對應的訊息。因此這部分需要作修改,重構一下。
讓 WatchDir extends Observable,把所觀測到的檔案訊息,包成一個 final 物件FileMonitorEvent,將訊息資料傳入個別的 Observer 作處理。作成 final 物件,是因為檔案異動訊息一產生,就不能被更改。圖中的 Observer (藍色部分),不一定需要實際的 class ,也可以用 lambda 或 anonymous 的方式。
此外,一個監控目錄,應該對到一個 OptionDialog,也就是之前提到的 UseCase 中,除了顯示檔案異動訊息之外,其它的動作設定,均會在這個 OptionDialog 中。而個別的 Observer 也會在這個 OptionDialog 實作。如此將來可以擴充成多個 OptionDialog ,就可以監控多個目錄。
再回頭看一下這兩個 Class Diagram,也許會有人認為,其實一開始是可以設計第二張圖的,為什麼不一開始就「設計好」呢?這個部分要思考一下幾個想法。
首先,每個人寫程式的方式其實是不一樣的,即使是同一人,每次作法也不見得會一樣。以這個監控檔案異動的程式,當初在寫的時候,「重點」是趕快寫出來,讓監控檔案異動的部分可以動再說,如果實際上 Java API 作不到,可能要採用其它的語言來寫。
第二,因為不了解實際 Java API 處理檔案監控的機制為何,所以一開始也無法很快了解要用到 Observer pattern 來處理「檔案異動訊息」,而原本的範例程式,就 for 一直跑,要修改成 Thread 來使用。再加上之前寫的都是 Server Side 的 Java, Swing 很久沒寫了,WindowBuilder 也才剛用個「幾天」,雖然很直覺,很好用,但總是要花時間摸索。如果不先把功能作出來,大概時間都會花在處理了解這些 Swing 元件 或 找 pattern 了。
第三,這其實是個小程式,因此初期不需要太多的設計,就可以很快實作出來,而會卡關的,除了對 API 不熟之外,還有環境的不了解,因此,當初寫的時候,「一不小心」就花了半天的時間去查「所有可以監控檔案」的程式寫作,包含 windows 的 API 等,最後才發現,就用 Java 自己的 API 就可以了。這樣寫程式,很容易「發散」沒有專注。
上述的想法,在寫作小程式或試驗的時候,是可以這樣處理的。大型專案就比較難這樣作。因為大型專案如果沒有作好設計,一開發下去,若有問題,人力物力還要重新投資。
從想到作,寫大概的 UseCase,程式實作出來,作 Class Diagram,然後再改成新版用 Observer pattern 也花了二天左右。真的作不對,整個打掉再來,也還能接受。
倒是寫這個 blog 文章花的時間比較多。因此,有一個構想點子,會比去了解更多有的沒有的技術,會更重要。因為這樣比較好聚焦在要處理的程式碼上。而技術的使用,在需求及功能初步達成後,再回頭重構也是可以的。
2016年6月22日 星期三
2016年6月19日 星期日
開發 File Monitor
加密勒索病毒 CryptoLocker 是很難對付的病毒,中毒之後,重要的檔案都會被加密,而解密的 key 在對方手上,除非防毒軟體有找到這些 key ,不然除了付錢給對方,要求提供解密功能,用其它的方式,幾乎是不可能回復檔案。
但加密檔案,一定會動檔案作異動。這個時候若有可以監控指定目錄下的檔案異動程式,就可以得知是否有可疑的異動。以便使用者進一步作處理。
因此 File Monitor 這個程式,就是因應這樣的需求而作的。
本程式是用 Java 語言,在 Eclipse 下寫作而成。
1. 首先是大概確認使用案例,這裡用簡單的 UML Use case 表示
使用者可以有三個動作,一是執行或停止監控,一是設定指定監控目錄,最後則是設定選項,可以指定目錄、指定檔名過瀘、顯示訊息、撥放聲音檔、執行程式等。先實作紅色框的部分。
2.使用 Eclipse ,建立 FileMonitor 專案,並用 WindowBuilder 拉出如下的視窗
畫面分成三個部分,
上方 panel,放 Monitor Directory: 文字和按鈕等元件
中間 scrollPane,放顯示監控到的檔案異動 textArea
下方 textPane,顯示訊息
3. 實作檔案監控的部分,需要使用到 WatchService API ,可參考 Watching a Directory for Changes ,使用它所提供
4.在 UI 使用 WatchDir,主要增加 button 的動作。
5. 實際的執行結果如下
但加密檔案,一定會動檔案作異動。這個時候若有可以監控指定目錄下的檔案異動程式,就可以得知是否有可疑的異動。以便使用者進一步作處理。
因此 File Monitor 這個程式,就是因應這樣的需求而作的。
本程式是用 Java 語言,在 Eclipse 下寫作而成。
1. 首先是大概確認使用案例,這裡用簡單的 UML Use case 表示
使用者可以有三個動作,一是執行或停止監控,一是設定指定監控目錄,最後則是設定選項,可以指定目錄、指定檔名過瀘、顯示訊息、撥放聲音檔、執行程式等。先實作紅色框的部分。
2.使用 Eclipse ,建立 FileMonitor 專案,並用 WindowBuilder 拉出如下的視窗
畫面分成三個部分,
上方 panel,放 Monitor Directory: 文字和按鈕等元件
中間 scrollPane,放顯示監控到的檔案異動 textArea
下方 textPane,顯示訊息
3. 實作檔案監控的部分,需要使用到 WatchService API ,可參考 Watching a Directory for Changes ,使用它所提供
WatchDir
example,原程式是命令模式執行,並用 System.out 輸出訊息,因此要修改為接收一個 textArea 作輸出。此外,把 WatchDir 實作 Runnable 界面,這樣就可以用 Thread 起動。因此也需加上 run() 的實作部分。4.在 UI 使用 WatchDir,主要增加 button 的動作。
btnStart = new JButton("Start"); btnStart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { wd = new WatchDir(Paths.get(textField.getText()), true, textArea); Thread t = new Thread(wd); t.setDaemon(true); t.start(); btnStart.setEnabled(false); btnStop.setEnabled(true); } catch (IOException e1) { textPane.setText(e1.getMessage()); } } }); panel.add(btnStart); btnStop = new JButton("Stop"); btnStop.setEnabled(false); btnStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { wd.stopRunning(); btnStart.setEnabled(true); btnStop.setEnabled(false); } }); panel.add(btnStop); btnClear = new JButton("Clear"); btnClear.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { textArea.setText(""); } }); panel.add(btnClear);
5. 實際的執行結果如下
寫程式「前」,先寫測試
1.程式是給其它的人用的,所以先寫測試,就是要讓自己先了解這程式「如何」使用。反過來說,如果測試很難寫,代表程式可能不好用,看是要修改規格,或是修改界面等。
2.先寫測試可以提昇程式開發人員「預見」程式的外部運作方式,有助於精進程式設計能力。
3.只有一類人可不寫測試,那就是設計天才,一出手就很完整,就不需測試。但這類人畢竟是少數,而若是團隊開發,再天才,程式還是得讓其它人使用,還是需要提供測試,這樣別人才知道天才的程式怎麼使用。
4.有時測試不見得是寫程式,也可以先作測試設計,了解到底要作的是什麼。
2.先寫測試可以提昇程式開發人員「預見」程式的外部運作方式,有助於精進程式設計能力。
3.只有一類人可不寫測試,那就是設計天才,一出手就很完整,就不需測試。但這類人畢竟是少數,而若是團隊開發,再天才,程式還是得讓其它人使用,還是需要提供測試,這樣別人才知道天才的程式怎麼使用。
4.有時測試不見得是寫程式,也可以先作測試設計,了解到底要作的是什麼。
2016年6月18日 星期六
開發 Firefox webextensions addon
Firefox 從 47 之後,採用了 webextensions API,以前一直使用的 XUL 等就廢除無法使用,使得不少依存在 XUL 的 addon 通通不能用了。而 webextensions 要到 48 版才會有穩定版本,也就是該有的 API 實作會出來。
目前使用的是 46.01 ,裡面已經有部分可以使用的 webextensions 。故記錄一下設定開發環境的過程,以便下次回頭查詢。
1.首先,建立另一個 Profile,執行 firefox.exe -P 新增 dev。然後就可以用 firefox.exe -no-remote -P dev 當作開發環境。
2.安裝 DevPrefs 把開發環境設定好,這樣就不用一個一個設定了
3.Options -> Advanced -> Update 設定永不檢查更新,尤其是在用舊版程式作開發時,一不小時,Firefox 就更新了。
4.在網址列輸入 about:debugging 用 Load Temporary Add-on 把 manifest.json 載入 。
5.把 about:debugging 放入書籤中,並設定顯示書籤列。
6.因為每次修改,均要重啟 firefox,用 firefox.exe -no-remote -P dev 起動後,按 about:debugging,按 Load ... Addon,再按 Ctrl+Alt+Shift+I 開啟 Browser Toolbox 的除錯工具。就可以測試 addon 了。
至於 addon 的寫作,可參考 Your first WebExtension ,裡面也有範本,但要注意,有些範本使用到的 API 要到 48 版才有,若是用比 48 還早的版本,不是每一個都能正常運作。
目前使用的是 46.01 ,裡面已經有部分可以使用的 webextensions 。故記錄一下設定開發環境的過程,以便下次回頭查詢。
1.首先,建立另一個 Profile,執行 firefox.exe -P 新增 dev。然後就可以用 firefox.exe -no-remote -P dev 當作開發環境。
2.安裝 DevPrefs 把開發環境設定好,這樣就不用一個一個設定了
3.Options -> Advanced -> Update 設定永不檢查更新,尤其是在用舊版程式作開發時,一不小時,Firefox 就更新了。
4.在網址列輸入 about:debugging 用 Load Temporary Add-on 把 manifest.json 載入 。
5.把 about:debugging 放入書籤中,並設定顯示書籤列。
6.因為每次修改,均要重啟 firefox,用 firefox.exe -no-remote -P dev 起動後,按 about:debugging,按 Load ... Addon,再按 Ctrl+Alt+Shift+I 開啟 Browser Toolbox 的除錯工具。就可以測試 addon 了。
至於 addon 的寫作,可參考 Your first WebExtension ,裡面也有範本,但要注意,有些範本使用到的 API 要到 48 版才有,若是用比 48 還早的版本,不是每一個都能正常運作。
2016年6月17日 星期五
研究 Scala 的一小部分想法
這幾天開始研究 Scala ,相較於 Javascript ,嚴謹多了,而它主要是基於 JVM 的下一代 Java 語言。其中有不少語法特性,值得思考。
比方像是 override 關鍵字,若子類別使用到父類別同名的成員變數或功能,則必需指定 override ,表示子類別「確實」要使用同名的變數或功能,而且要自己實作這個功能。這樣的語法,使得軟體工程師在寫程式,一定會注意到是否寫到 override 的部分。
以前作大型專案時,曾經有個網頁程式一直出錯,但很難查出問題在那。後來就從頭一步一步查起,結果是在前端的 javascript 中,有一個功能的名字,跟使用到的 javascript lib 「一模一樣」,但因為 javascript 沒有「強制」override 的識別功能,所以,程式跑起來就不穩定,只要使用到這個同名功能時,就會出錯。
另一個看到的是 Structural typing。這個跟 Groovy 的 duck 的概念類似,也就是,如果走起來像個鴨子,叫聲像個鴨子。那它就應該是個鴨子。Scala 的 Structural typing,可以自行定義一個結構,來表示這些行為,從而使用這些動作,即使是沒有關係的類別,只要符合結構定義的行為,就可以使用這個行為。
這樣的特性,有什麼用處?有時候,我們需要增加既有的程式一些功能,但原程式並沒有提出 interface 之類的元件,可以讓我們用繼承的方式使用時,就可以藉由結構,把需要的「行為」定義出來,這樣就可以增加新的類別元件,採用跟原程式庫中相同的「行為」名稱,而不需動到既有的程式庫。
比方像是 override 關鍵字,若子類別使用到父類別同名的成員變數或功能,則必需指定 override ,表示子類別「確實」要使用同名的變數或功能,而且要自己實作這個功能。這樣的語法,使得軟體工程師在寫程式,一定會注意到是否寫到 override 的部分。
以前作大型專案時,曾經有個網頁程式一直出錯,但很難查出問題在那。後來就從頭一步一步查起,結果是在前端的 javascript 中,有一個功能的名字,跟使用到的 javascript lib 「一模一樣」,但因為 javascript 沒有「強制」override 的識別功能,所以,程式跑起來就不穩定,只要使用到這個同名功能時,就會出錯。
另一個看到的是 Structural typing。這個跟 Groovy 的 duck 的概念類似,也就是,如果走起來像個鴨子,叫聲像個鴨子。那它就應該是個鴨子。Scala 的 Structural typing,可以自行定義一個結構,來表示這些行為,從而使用這些動作,即使是沒有關係的類別,只要符合結構定義的行為,就可以使用這個行為。
def doQuack(o: {def quack(value: String): String}) { println (o.quack("Quack")) }
class NorthDuck { def quack = "quack" } class SouthDuck { def quack = "quack quack" }
doQuack(NorthDuck) doQuack(SouthDuck)
這樣的特性,有什麼用處?有時候,我們需要增加既有的程式一些功能,但原程式並沒有提出 interface 之類的元件,可以讓我們用繼承的方式使用時,就可以藉由結構,把需要的「行為」定義出來,這樣就可以增加新的類別元件,採用跟原程式庫中相同的「行為」名稱,而不需動到既有的程式庫。
2016年6月16日 星期四
Javascript是個有「洞」的語言
直到 ECMAScript 2015 出來之前,Javascript 一直是個有洞的語言,它很容易讓程式出錯,讓開發人員看不懂程式碼的用意,尤其是一些「所謂」的技巧
5 nifty JavaScript tricks that you may not know
比方像第一個 case
改寫成底下
寫程式,是要解決問題,而不是解決程式的問題。
由於 Web 前端都採用 javascript 處理,故使用這個語言,最佳的策略便是使用「高手們」作出的套件,愈高階愈好,像JQuery比較低階。而AngularJS 就比較高階。因為採用高階套件,就不需寫太多javascript程式碼,就可以避免出錯。Google 有 GWT,可以用 Java 寫 javascript ,可惜它的 Designer 及似乎停止發展,更新緩慢。這可能是因為後起之秀 Vaadin 有開發付費版本的 Designer ,要避免競爭之故吧。
但現在的程式開發,重點不是開發,因為現今能用的技術,幾乎都能很快速實作出需要的功能,而只有好的點子的實作,才會有最多人使用。因此,如果構思好點子,會比使用這些炫麗的功能更為重要。
5 nifty JavaScript tricks that you may not know
比方像第一個 case
if(fruit === 'apple' || fruit === 'banana' || fruit === 'chikoo'){ doMagic(); }
改寫成底下
if({apple:1,banana:1,chikoo:1}[fruit]){ doMagic(); }這樣作的目的是讓原來的文字檢查,變成用一個 {} 物件的 key / value 檢查,但這種寫法,其它的語言不一定能直接實作,尤其是現在是跨語言使用的時代,愈多特定語法的使用,愈不能在別的語言運用。同時,這種技巧也愈需要花時間了解。
寫程式,是要解決問題,而不是解決程式的問題。
由於 Web 前端都採用 javascript 處理,故使用這個語言,最佳的策略便是使用「高手們」作出的套件,愈高階愈好,像JQuery比較低階。而AngularJS 就比較高階。因為採用高階套件,就不需寫太多javascript程式碼,就可以避免出錯。Google 有 GWT,可以用 Java 寫 javascript ,可惜它的 Designer 及似乎停止發展,更新緩慢。這可能是因為後起之秀 Vaadin 有開發付費版本的 Designer ,要避免競爭之故吧。
但現在的程式開發,重點不是開發,因為現今能用的技術,幾乎都能很快速實作出需要的功能,而只有好的點子的實作,才會有最多人使用。因此,如果構思好點子,會比使用這些炫麗的功能更為重要。
2016年6月15日 星期三
Eclipse Java Swing BeansBinding JSR 295
UI 元件常需跟 Model 的資料作連動,除了自己寫程式之外,也可以用 Eclipse WindowBuilder + BeansBinding 提供的方式作連動。
底下是簡單的範例,當使用者輸入最上方的文字方塊,最下方會同步顯示輸入的文字。按下 Add 後,該文字就會置入中央的列表中,並清空文字方塊。
1.首先要加裝 jsr 295 Beans Binding 的實作,選用的是 BetterBeansBinding ,如果使用 Gradle,則要設定 BetterBeansBinding SwingBinding
2.執行 gradlew.bat 把 jar 檔設定好後,隨便拉一下 Swing 組成如下的視窗,可以看到有個Bindings的tab
3. 把 Target 跟 Model 的 text 綁定起來。
4.底下是自動產生的程式碼片斷,怎麼解讀?算了,不重要,有興趣自己去看 JSR 295,只要它能讓兩個元件彼此資料連動就好了。
好啦,這樣就可以連動兩個元件的資料了。
不過 JSR 295 好像熄火了,沒什麼動作,所以這些程式庫也很久沒有新版。目前比較「熱」的是 JGoodies Binding ,至於怎麼用,下次再研究吧 ......
底下是簡單的範例,當使用者輸入最上方的文字方塊,最下方會同步顯示輸入的文字。按下 Add 後,該文字就會置入中央的列表中,並清空文字方塊。
1.首先要加裝 jsr 295 Beans Binding 的實作,選用的是 BetterBeansBinding ,如果使用 Gradle,則要設定 BetterBeansBinding SwingBinding
2.執行 gradlew.bat 把 jar 檔設定好後,隨便拉一下 Swing 組成如下的視窗,可以看到有個Bindings的tab
3. 把 Target 跟 Model 的 text 綁定起來。
4.底下是自動產生的程式碼片斷,怎麼解讀?算了,不重要,有興趣自己去看 JSR 295,只要它能讓兩個元件彼此資料連動就好了。
好啦,這樣就可以連動兩個元件的資料了。
不過 JSR 295 好像熄火了,沒什麼動作,所以這些程式庫也很久沒有新版。目前比較「熱」的是 JGoodies Binding ,至於怎麼用,下次再研究吧 ......
2016年6月13日 星期一
Eclipse WindowBuilder 的使用心得
Eclipse 預設在建立 Swing 物件時,會把一切組件宣告通通放在 constructor 中。
但有時要特別處理某組件,我們可以將它 expose 出來,也就是多一個 get method 並把該組件宣告成 class 成員變數,如果沒有外部程式使用 get method,那就刪除那個 get method。另外使用 private method 操作該組件。
例如:
1.底下是一個簡單的 Swing 程式
2.在 Eclipse 的程式碼如下,可見綠色框的部分,已經是成員變數,而紅色框的部分,則定義在method中
3. 回到 Desgin 模式,將 textArea Expose 出來
4. 切換到程式碼模式,就可以看到改變的部分
5. 此時就可以在整個 class 中,使用 textArea ,因為它已經是成員變數了
如果希望每一個組件都是成員變數,可以由 Window -> Preferences -> WindowBuilder -> Code Generation 中,設定 Field 模式即可
這樣產生出來的組件,都會是成員變數了。
但有時要特別處理某組件,我們可以將它 expose 出來,也就是多一個 get method 並把該組件宣告成 class 成員變數,如果沒有外部程式使用 get method,那就刪除那個 get method。另外使用 private method 操作該組件。
例如:
1.底下是一個簡單的 Swing 程式
2.在 Eclipse 的程式碼如下,可見綠色框的部分,已經是成員變數,而紅色框的部分,則定義在method中
3. 回到 Desgin 模式,將 textArea Expose 出來
4. 切換到程式碼模式,就可以看到改變的部分
5. 此時就可以在整個 class 中,使用 textArea ,因為它已經是成員變數了
如果希望每一個組件都是成員變數,可以由 Window -> Preferences -> WindowBuilder -> Code Generation 中,設定 Field 模式即可
這樣產生出來的組件,都會是成員變數了。
分時分工專注
想要一段長時間的專注在單一事件上,有時不是容易的事,比如寫程式就是一例,此時可以採用分時分工專注的方法,也就是,當無法專注在寫程式時,就去作簡單的運動,專注在運動上,然後再回頭寫程式,或許會有進展。此外看其它文章,聽音樂,寫blog等等,都可以分時分工專注。當然重要的眼睛休息時刻也要加進去,這樣應該會比較有效率些。
至於不同的事件花多少時間,這個就看自己掌握了。
至於不同的事件花多少時間,這個就看自己掌握了。
2016年6月12日 星期日
減少冗長的樣板程式碼 - Lombok
寫 Java 程式時,常會需要為成員變數建立 getter 及 setter,使用 IDE 工具可以容易自動建立這些樣板程式碼,但當成員變數過多時,整個程式碼就會變得很長,而真正運作的程式碼,就會淹沒在其中。有個 Lombok 的專案,針對這個部分,作了一系列的 annotations。效果顯示如下:
1.未使用前,右方只有一堆成員變數。
2.使用 @Data 後,自動產生樣板程式碼,顯示在右方 method 清單上,但左方並沒有產生這些程式碼,都被 @Data 代表了。
3.而底下就是真正執行功能的程式,整個程式碼清爽多了
專案主頁
https://projectlombok.org/
詳細說明
http://jnb.ociweb.com/jnb/jnbJan2010.html
1.未使用前,右方只有一堆成員變數。
2.使用 @Data 後,自動產生樣板程式碼,顯示在右方 method 清單上,但左方並沒有產生這些程式碼,都被 @Data 代表了。
3.而底下就是真正執行功能的程式,整個程式碼清爽多了
專案主頁
https://projectlombok.org/
詳細說明
http://jnb.ociweb.com/jnb/jnbJan2010.html
2016年6月5日 星期日
軟體建構之道 (Code Complete, 2/e)
這幾天開始讀這部軟體工程巨作。目前只看到第五章,但其實很多觀念在專案中,不時會使用到。如果說沒有開發程式的經驗,就先看這本書,是不錯。但經過專案及程式開發後再來看,會更有啟發性。
訂閱:
文章 (Atom)