顯示具有 javascript 標籤的文章。 顯示所有文章
顯示具有 javascript 標籤的文章。 顯示所有文章

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月4日 星期一

Javascript OOP 最佳實作

OOP In JavaScript: What You NEED to Know

本文作者將 javascript OOP 的最佳實作寫成例子,日後寫程式時,可以參考
將 property 宣告在 function 中,將 method 宣告在 function.prototype 中


此外用 Google best javascript inheritance 還有許多文章。

3 ways to define a JavaScript class
不使用的方式
1.將 method 獨立宣告,然後 在 function 中引用,這會造成 gobal 中有許多 method。
2.在 function 宣告 method,這會造成每一次 new 就再產生該 method 。

建議方式,將 method 用 function.prototype 方式宣告,這樣只保有一個 method。而 property 宣告在 function ,這樣就每 new 一個 function ,就產生新的 property 實例。

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;
    }
  };
 
})();