顯示具有 開發 標籤的文章。 顯示所有文章
顯示具有 開發 標籤的文章。 顯示所有文章

2016年7月19日 星期二

Learning JavaScript Design Patterns 筆記 6

The Mixin Pattern

不透過繼承的方式,將其它物件的部分 method 「拿來使用」。此模式主要目的是「程式碼重用」。但也因為如此,會讓物件關係變得比較混亂。因此,如果沒有必要,這模式最好少使用。可能的使用時機是,「除錯」或是,太複雜的原程式碼只能透過借用的方式處理。

使用 underscorejs 的 extend

// 要被「借用method」的物件
var myMixins = {
  moveUp: function(){ /* ... */ },
  moveDown: function(){ /* ... */ },
  stop: function(){ /* ... */ }
};

// 借用方
function CarAnimator(){
  this.moveLeft = function(){ /* ... */ };
} 
 
// 將 myMixins 的方法給借用方
_.extend( CarAnimator.prototype, myMixins );
 
var myAnimator = new CarAnimator();
// 使用「借用」的method
myAnimator.moveLeft();
myAnimator.moveDown();
myAnimator.stop();


另外此部分有提供只借用部分method的方式,但將欲借用的method,當作第三個參數以後傳入。可是這個function只有定義兩個參數,雖然javascript可以用這樣的方式來接收「多」出來的參數作處理,但最好還是定義第三個參數,直接處理比較好。免得日後還要猜第三個未定義的參數,到底是什麼意思。

// t: targetClass, s:sourceClass, m:methods array
function augment(t, s, m) {
    // 只需部分 methods
    if (m) {
        for (var i=0; i < m.length; i++ ) {
            t.prototype[m[i]] = s.prototype[m[i]];
        }
    }
    // 將 s 的 all methods 傳給 t
    else {
        for (var mName in s.prototype ) {
            if ( !Object.hasOwnProperty.call(t.prototype, methodName) ) {
                t.prototype[mName] = s.prototype[methodName];
            }
        }
    }
}

// 使用方式
augment(target, source, ["methodA", "methodB"]);
augment(target, source);

2016年7月17日 星期日

Learning JavaScript Design Patterns 筆記 5

The Command Pattern

原文中,此部分只是把一個存在的物件,另外包起來而已,應參考Command pattern的實作方式。分成下列三個物件:
  • 管控命令的物件(Invoker):
    • 加入、記錄、執行Command的物件。
    • 可將 Command 的實例「抽出」成一個 String 對應的參數,如此在 Client 端就完全不需知道實際使用到的 Receiver Class
  • 命令物件(Command):
    • 均實作Command Interface,有共同的一個method -- execute
    • 由 Invoker 呼叫 execute。
  • 執行命令的物件(Receiver)
    • 在 Command 的 execute 中,實際運作的物件。
    • 這些物件可能有不一樣的constructor,不一樣的參數設定,但都需在 Command execute 之前,建構完成。
範例可直接參考 Wiki 
圖例可參考 Google 查詢


The Facade Pattern 

「表面」模式 ,將「特定」物件複雜的API,透過表面模式,包裝成一個較易於了解和使用的物件。如 JQuery 把許多複雜的判斷 DOM 操作,變成單一 method 呼叫,例如 $(el).css(),$(el).animate()。

表面模式的使用,要從 Client 的角度來看,由於複雜物件及 API ,在使用上可能需一步一步設定,才能達到「某」特定功能。而對 Client 端,它「只」需這「某」特定功能。故透過表面模式,將此「某」特定功能「包」起來,讓 Client 直接使用。

效能注意,以JQuery為例,直接使用 getElementById("identifier") 比 $("#identifier") 快很多很多

發射飛彈的流程 ,每一層都可用表面模式
  • 對發射控制官而言,它需要長官確認,核對密碼,插入Key等等一連串動作,最後讓長官按發射鈕。但他不用去管實際飛彈要發射或發射時的一連串機械動作。
  • 對長官而言,他只需要總統的命令,將密碼提供給發射官,最後按按鈕。
  • 對總統而言,只是打個電話,要求發射飛彈。


The Factory Pattern

不直接使用 new ,而是透過這個 Factory pattern 來取得物件實例,何時使用:
  • 當建立此物件實例的過程很繁雜
  • 需依環境需求,同一物件需有不同的參數設定時
  • 當共用相同properties的許多小物件時
  • 建立同interface但不同的實例實作時
Abstract Factory Pattern v.s. Factory Pattern
  • Abstruct Factory Pattern 將物件的生成,用一個Class處理。因此個別實作的 Factroy 可產生個別的物件。
  • Factory Pattern 將物件的生成,用一個Method處理,所以只能針對特定物件的生成作處理。 
參考 Abstract Factory

2016年7月12日 星期二

Learning JavaScript Design Patterns 筆記 4

The Mediator Pattern

透過中介者,將多個物件的運作及處理流程寫在其中,例如飛機彼此的通訊,全通過塔台統一處理,而不是由個別的飛機互相溝通。

Mediator:
  • 處理流程主要由此處理
  • 協調個別執行的物件
  • 此模式使用到的物件,彼此多少有關係,才會有處理流程可放在Mediator中

Event Aggregation:
  • 處理流程主要藉由 event 傳給由各個Subscriber處理
  • 本身不含處理流程
  • 像是 JQuery 的 on,可以讓個別的 HTML 元件,透過這個 on 而有自己的 event 處理機制
  • 物件彼此間較無直接關連


The Prototype Pattern

The GoF refer to the prototype pattern as one which creates objects based on a template of an existing object through cloning.

使用 Object.create 的方式,在原有的程式(物件)上,產生新的物件。
實作方式應採用 Javascript OOP 最佳實作 ,此篇主要介紹 prototype 的概念。

Learning JavaScript Design Patterns 筆記 3

Publish/Subscribe Implementations

pubsubz/pubsubz.js

key point:
publish(topic, args): 執行特定topic的所有subscriber的func,並將參數args傳入
subscribe(topic, func): 將特定topic跟func綁定,以便publish中使用
unsubscribe: 移除 subscriber

Usage:
// 要綁定的 func
var mlog = function (t, d) { console.log(t+ ": " + d);};
// 綁定 topic (inbox/newMessage) 跟 mlog
var subscription = pubsub.subscribe("messagee", mlog);
// publish !
pubsub.publish("message", "hello world!");
pubsub.publish("message", ["test", "a", "b", "c"]);

2016年7月9日 星期六

Learning JavaScript Design Patterns 筆記 2

The Observer Pattern


GoF book, Design Patterns: Elements of Reusable Object-Oriented Software :
"One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves."

--------------------------------------------------
function OL(){ this.ol = []; }
OL.prototype.add     = function(o) { return this.ol.push( o ); };
OL.prototype.remove  = function(i) { this.ol.splice( i, 1 ); };
OL.prototype.count   = function() { return this.ol.length; };
OL.prototype.get     = function(i) { if( i > -1 && i < this.ol.length ){ return this.ol[i]; } };
OL.prototype.indexOf = function(o) {
  for(var i; i < this.ol.length; i++) { 
    if( this.ol[i] === o ) { return i; } 
    return -1; 
  }
};

// Maybe just extend OL class or using [] as new OL() ?
function Subject() { this.os = new OL();}
Subject.prototype.addObserver    = function( o ){ this.os.add( o ); };
Subject.prototype.removeObserver = function( o ){ this.os.remove( this.os.indexOf(o) ); };
Subject.prototype.notify         = function( context ){
  var observerCount = this.os.count();
  for(var i=0; i < observerCount; i++){
    this.os.get(i).update( context );
  }
};

// The Observer
function Observer(){
  this.update = function(){
    // ...
  };
}

--------------------------------------------------
// Extend an object with an extension
function extend(obj, extension){
  for (var key in extension){
    obj[key] = extension[key];
  }
}

// Concrete Subject
// Extend the controlling checkbox with the Subject class
extend( aSubject, new Subject() );

// aFunction will trigger notifications and passing data to its observers
aSubject.aFunction = function(){ aSubject.notify( aSubject.data ); };

// aObjAction will add observer to aSubject
aObjAction.aMethod = addO;
 
// Concrete Observer
function addO(){
  // Extend the aO with the Observer class
  extend(aO, new Observer());
 
  // Override with custom update behaviour
  aO.update = function( value ){ alert(value); };
 
  // Add observer to aSubject
  aSubject.addObserver(aO);
}

--------------------------------------------------
// When aObjAction.aMethod is called, aO observer is added to aSubject
// When aSubject.aFunction is called, aO will alert its data

Differences Between The Observer And Publish/Subscribe Pattern

Observer pattern: observer must subscribe itself to the subject firing the event.
Publish/Subscribe pattern: use a topic/event channel, subscriber received notifications and publisher firing the event.

2016年7月7日 星期四

為什麼寫程式跟作管理很難一人兼職?

這跟工作的特性有關

寫程式是一個非常注重專注力的工作,往往寫一個程式段落要花數小時,甚至到半天一天都有。

作管理是一個非常鎖碎的工作,這個問題解決了,下一刻又來新問題,每個問題都有不同處理方式,有時需找尋許多不同的資訊,並且不定時要跟團隊成員溝通。

這兩者對特性如此不同,長期下來,是很難由一個人承擔的。

2016年7月3日 星期日

Learning JavaScript Design Patterns 筆記

Learning JavaScript Design Patterns 說明許多javascript模式,在此作些筆記。

由於Javascript是個有「洞」的語言,因此試圖去理解別人寫的javascript程式碼,無疑是給自己找麻煩,而自己寫javascript若沒有採用好的模式,將來作維護時,也很容易就發生看不懂的狀況。因此,寫 javascript 之前,先了解可用的模式,是「初學者」必需要作的第一步。當然,直接看模式對初學者來說,會比較辛苦些,但至少看過一遍後,寫的程式比較有機會將來可以維護。以前作大型專案時,若前端用很多javascript,在作程式改版或維護時,至少80%就是打掉重寫! 跟 Java 比,javascript困難太多了。

由於該書的程式碼用長名稱,使得模式的「樣子」不易一眼看出來,所以將此書中的模式,用較短命名重寫,重點是方便一眼看出模式的樣子,實際命名時還是要注意使用常用的命名方式。


What is a Pattern? 什麼是模式
  • Patterns are proven solutions 模式已驗證可提供解決方案
  • Patterns can be easily reused 模式易於理解
  • Patterns can be expressive 模式易於表達

The Constructor Pattern

// 1. Object Creation
var a = {};
var a = Object.create( Object.prototype );
var a = new Object();  

// Object keys and values
// 2.1 dot
a.k = "v";
var value = a.k;

// 2.2 Square bracket
a["k"] = "v";
var value = a["k"];

// 2.3 Object.defineProperty
Object.defineProperty( a, "k", {
    value: "v",
    writable: true,
    enumerable: true,
    configurable: true
});

// 2.4 Object.defineProperties
Object.defineProperties( a, {
  "k": { value: "v", writable: true },
  "l": { value: "w", writable: false}
});

// 3 Basic Constructors
function A(i, j) {
  this.i = i;
  this.j = j;
  this.total = function () { return this.i + this.j; };
  this.max   = function () { return Math.Max(this.i, this.j); };
}

// 4 Constructors With Prototypes
function A(i, j) {
  this.i = i;
  this.j = j;
}
A.prototype.total = function () { return this.i + this.j; };
A.prototype.max   = function () { return Math.Max(this.i, this.j); };


The Module Pattern

// Object Literals
var a = {
    k: "v",
    f: function () { return k; }
};

// The Module Pattern
var a = (function () {
  var k = "v";
  function up() { k = k.toUpperCase(); }
  return {
    publicK: "value",
    getK:   function () { return k; },
    setK:   function (value) { k = value; },
    getUpK: function () { up(); return k; }
  };
})();

// Import mixins
var a = (function (b, c) {
  var k = "v";
  function up() { k = k.toUpperCase() + b + c; }
  return {
    publicK: "value",
    getKBC: function () { return k + b + c; },
    setK:   function (value) { k = value; },
    getUpK: function () { up(); return k; },
  };
})(b ,c);

// Exports
var a = (function () {
  var b = {};

  var k = "v";
  function up() { k = k.toUpperCase(); }

  b.publicK = "value";
  b.getK    = function () { return k; };
  b.setK    = function (value) { k = value; };
  b.getUpK  = function () { up(); return k; }

  return b;
})();


The Singleton Pattern

var singleton = (function () {
  var instance;

  function init() {
    var k = "v";
    function privateM(){ /* ... */ }
    return {
      publicK: "public",
      publicM: function () { privateM(); }
    };
  };
 
  return {
    getInstance: function () {
      if ( !instance ) { instance = init(); }
      return instance;
    }
  };
 
})();

2016年6月22日 星期三

開發 File Monitor 2

之前完成的程式,用 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月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 ,使用它所提供 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. 實際的執行結果如下

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 還早的版本,不是每一個都能正常運作。