Skip to content

Instantly share code, notes, and snippets.

@tingwei628
Last active June 13, 2018 17:17
Show Gist options
  • Save tingwei628/059b2e62f797103f83e80e9478e4075d to your computer and use it in GitHub Desktop.
Save tingwei628/059b2e62f797103f83e80e9478e4075d to your computer and use it in GitHub Desktop.

Thanks to "Learning JavaScript Design Pattern" @Addy Osmani

用一句講設計模式

“Pattern 提供“某些特定”情況的可重複利用解法

  1. 可重複使用
  2. 可以被傳達 (換句話, 節省developer溝通的時間)
  3. 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)

Pattern 分類

  • Creational Design Patterns

創造物件

  • Structural Design Patterns

物件關係

  • Behavioral Design Patterns

物件行為溝通

  • Constructor Pattern

example:

  1. var obj = {}; // use Object.defineProperty
  2. by its constructor // keyword: new
  3. 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 只能被創造一次!

  • 如何做?
  1. 如果不存在該object(由class創造), 則 new 一個
  2. 如果存在, 則直接返回該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/)

  1. https://github.com/millermedeiros/js-signals/wiki/Comparison-between-different-Observer-Pattern-implementations

*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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment