Thanks to "Learning JavaScript Design Pattern" @Addy Osmani
用一句講設計模式
“Pattern 提供“某些特定”情況的可重複利用解法
- 可重複使用
- 可以被傳達 (換句話, 節省developer溝通的時間)
- DRY(Don't Repeat Yourself)
我自己理由: code tracing and reviewing
每天都有用到Pattern
-
jQuery(Facade pattern) (Facade念做“ㄈ薩的”:意思是正面的) (jQuery 用來當抽象界面)
-
$("selector") v.s. getElementById()
如何證明該Pattern 是有效的?
Rule of three:
- pattern 如何有效?
- pattern 為什麼有效?
- Applicability - 怎麼被應用(非常重要!)
? Anti-Pattern 反模式
- 描述不好的解法(模式)導致不好的情況
(developer 要極力避免)
- 再來描述用好的解法(模式)解決不好情況
(by wiki)
-
行動、過程和結構中的一些重複出現的乍一看是有益的,但最終"得不償失"的模式
-
在實踐中證明且可重複的清晰記錄的重構方案 (幫助避免重蹈覆轍)
-
或者直接說"陷阱"!
簡單的說:
上下文(context) 定義問題
同一個方法對好的context 也許是好的解法; 但對錯誤的context 就是反模式
- 用全域變數容易污染
Passing strings rather than functions to either setTimeout or setInterval (http://stackoverflow.com/questions/6081560/is-there-ever-a-good-reason-to-pass-a-string-to-settimeout)
-
use of eval() internally. (https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/)
-
修改 Object class prototype
-
document.createElement v.s. document.write(反模式) (http://ithelp.ithome.com.tw/articles/10095309) after render 之後 will call it and override the page not work with XHTML (add html comment will solve it)
Pattern 分類
- Creational Design Patterns
創造物件
- Structural Design Patterns
物件關係
- Behavioral Design Patterns
物件行為溝通
- Constructor Pattern
example:
- var obj = {}; // use Object.defineProperty
- by its constructor // keyword: new
- by its prototype // keyword: new
- Module Pattern
as we know, ... commonJS AMD ES Harmony module
example: public/private:
var testModule = (function () {
var counter = 0; // private, you cannot access here!
// (also know local scope)
return {
incrementCounter: function () { // public
return counter++;
},
resetCounter: function () { // public
console.log( "counter value prior to reset: " + counter );
counter = 0;
}
};
})(); // IIFE (Immediately Invoked Function Expression)
testModule.counter // undefined
-
advantage: OOP !
-
disadvantage: bad for debugging those private variables
-
The Revealing Module Pattern
-
The Singleton Pattern
同一類的class 只能被創造一次!
- 如何做?
- 如果不存在該object(由class創造), 則 new 一個
- 如果存在, 則直接返回該object
- 有哪些應用? ANS: Thread Pool、Cache、Registry、Login User
這種只能"共用"!
example:
var mySingleton = (function () {
// Instance stores a reference to the Singleton
var instance; // <----- it should be private in case for overriding in public!
function init() {
// Singleton
// Private methods and variables
function privateMethod(){
console.log( "I am private" );
}
var privateVariable = "Im also private";
var privateRandomNumber = Math.random();
return { // <----------Module Pattern here
// Public methods and variables
publicMethod: function () {
console.log( "The public can see me!" );
},
publicProperty: "I am also public",
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
// Get the Singleton instance if one exists
// or create one if it doesn't
getInstance: function () {
if ( !instance ) { // 關鍵
instance = init();
}
return instance; // 返回該instance
}
};
})();
var singleA = mySingleton.getInstance();
var singleB = mySingleton.getInstance();
singleA === singleB // true
- Why is deferring(延遲) execution considered important for a Singleton?
ANS: 節省資源...
- A static instance of a class (object) v.s. a Singleton 差在哪?
http://stackoverflow.com/questions/519520/difference-between-static-class-and-singleton-pattern
靜態方法不需要實體化 Singleton 則需要
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static distance(a, b) { // static
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.sqrt(dx*dx + dy*dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
console.log(Point.distance(p1, p2));
-
Singleton's advantage and disadvantage?
-
The Observer Pattern
原因: Observer 對其Subject 的某狀態有興趣, 一旦Subject其狀態更新, 即通知Observer (by GOF)
角色: Object, Observer
行為: Observer ---->對Subject訂閱(Subscribe)/註冊(Register) Subject -----> 對Observer通知(notify)
Observer訂閱, 則Subject 加入該Observer
Observer取消訂閱, 則Subject 移除該Observer
Subject 裏面有一堆 Observers 等著通知
Observer List
function ObserverList(){
this.observerList = [];
}
ObserverList.prototype.add = function( obj ){
return this.observerList.push( obj );
};
ObserverList.prototype.count = function(){
return this.observerList.length;
};
ObserverList.prototype.get = function( index ){
if( index > -1 && index < this.observerList.length ){
return this.observerList[ index ];
}
};
ObserverList.prototype.indexOf = function( obj, startIndex ){
var i = startIndex;
while( i < this.observerList.length ){
if( this.observerList[i] === obj ){
return i;
}
i++;
}
return -1;
};
ObserverList.prototype.removeAt = function( index ){
this.observerList.splice( index, 1 );
};
Subject
function Subject(){
this.observers = new ObserverList();
}
Subject.prototype.addObserver = function( observer ){
this.observers.add( observer );
};
Subject.prototype.removeObserver = function( observer ){
this.observers.removeAt( this.observers.indexOf( observer, 0 ) );
};
Subject.prototype.notify = function( context ){
var observerCount = this.observers.count();
for(var i=0; i < observerCount; i++){
this.observers.get(i).update( context );
}
};
Observer
function Observer(){
this.update = function(message) {
console.log('This is ' + this.name );
console.log('I got message:' + message );
};
}
// 接著 繼承這些 prototype 實作 :)
function SexySubject() {
Subject.call(this);
}
function DirtyObserver(name) {
Observer.call(this);
this.name = name;
}
// 繼承
var tempSubjectPrototype = Object.create(Subject.prototype);
tempSubjectPrototype.constructor = SexySubject;
SexySubject.prototype = tempSubjectPrototype;
// 繼承
var tempDirtyObserverPrototype = Object.create(Observer.prototype);
tempDirtyObserverPrototype.constructor = DirtyObserver;
DirtyObserver.prototype = tempDirtyObserverPrototype;
var s = new SexySubject();
var o = new DirtyObserver('duck');
var o1 = new DirtyObserver('duck1');
s.addObserver(o);
s.addObserver(o1);
s.notify("HI");
// This is duck
// I got message:HI
// This is duck1
// I got message:HI
- Differences Between The Observer And Publish/Subscribe Pattern ?
Pub/Sub 有一個Event Channel, 允許定義不同事件 請查看 (http://developers-club.com/posts/270339/)
*Advantage and disadvantages?
advantages: loose coupling
disadvantages: 訂閱者如果發生錯誤, 發布者會不知道...因為decoupling
Pub/Sub (1) subscribe (2) unsubscribe (3) publish 應用於 DOM event handling
(請參考:https://developer.mozilla.org/zh-TW/docs/Web/Guide/Events/Creating_and_triggering_events)
實作看看~
var pubsub = {};
(function(myObject) { // <--- Channel
var topics = {};
var subUid = -1;
myObject.publish = function(topic, args) {
if ( !topics[topic] ) {
return false;
}
var subscribers = topics[topic],
len = subscribers ? subscribers.length : 0;
while (len--) {
subscribers[len].func( topic, args );
}
return this;
};
myObject.subscribe = function(topic, func) {
if (!topics[topic]) {
topics[topic] = [];
}
var token = ( ++subUid ).toString();
topics[topic].push({
token: token,
func: func
});
return token;
};
myObject.unsubscribe = function(token) {
for ( var m in topics ) {
if ( topics[m] ) {
for ( var i = 0, j = topics[m].length; i < j; i++ ) {
if ( topics[m][i].token === token ) {
topics[m].splice( i, 1 );
return token;
}
}
}
}
return this;
};
}(pubsub));
var callback = function(type, data) {
console.log("The event type is: "+ type);
console.log("This event data is: "+ data);
};
var mySub = pubsub.subscribe("eventType1", callback);
var mySub = pubsub.subscribe("eventType2", callback);
pubsub.publish("eventType1", "Here is eventType1 data");
pubsub.publish("eventType2", "Here is eventType2 data");
// The event type is: eventType1
// This event data is: Here is eventType1 data
// The event type is: eventType2
// This event data is: Here is eventType2 data