2017年10月12日 星期四

[Design Pattern] 獨體模式 (Singleton Pattern)

程式設計的時後,有些物件只能被實體化(new)一次
像是登錄(registry)物件、偏好設定物件等等
這些系統資訊的物件,常被其它的物件實體化取得資訊
在每個物件要取得資訊的時後都要先new一次才能用get()方法取得資訊
如果這個物件在很多處都有用到的話
那就會被實體化(new)很多次,可能會有很多問題產生
如:程式行為異常、資源使用過量或是不一致的問題

那要如何避免這樣問題呢?
工程師之間約定好只能實體化一次 : 這不用說問題有多大了XD
全域變數 : 在一開頭就要先建立好物件,不論是否會用到這個物件,這樣會造成資源的浪費
獨體化 : 可以在有使用到的時後才建立物件,有效的利用資源避免浪費

獨體化是利用靜態類別變數、靜態方法以及一些存取的修飾,來達到保証物件只被實體化一次

獨體設計根據不同問題的適用性有三種設計方法

  • 同步化getInstance()
public class Singleton {
  private static Singleton uniqueInstance;

  private Singleton(){}
  
  public static synchronized Singleton getInstance(){
    if (uniqueInstance == null){
      uniqueInstance = new Singleton();
    }
    return uniqueInstance;
  }
}

synchronized 關鍵字的作用就是同步化
會確認所有呼叫getInstance()都結束後才會繼續執行接下來的程式
這可以避免A物件正在建立Singleton的空窗期中,有其它物件正在檢查Singleton是否被建立
造成不一致的問題
但如果很頻繁會呼叫getInstance(),每次都要等待後才會處理
選用這個方法這樣會降低效率
  • 率先實體化(Eagerly instants)
public class Singleton {
  private static Singleton uniqueInstance = new Singleton();

  private Singleton(){}

  public static Singleton getInstance(){
    return uniqueInstance;
  }
}

在一開始進入Singleton類別的時後就先實體化類別,無論是否真的有使用到
如果確定一定要有一個Singleton類別,用這個方法也不錯。

  • 雙重檢查上鎖(Double-checked locking)
public class Singleton {
  private volatile static Singleton uniqueInstance;
  
  private Singleton(){}

  public static Singleton getInstance(){
    if (uniqueInstance == null){
      synchronized (Singleton.class) {
        if (uniqueInstance == null){
          uniqueInstance = new Singleton();
        }
      }
    }
    return uniqueInstance;
  }
}

如果沒有效能上的考量就不需要用這個方法,不然有種殺雞用牛刀的感覺
用這個方法要注意要用JAVA 2的版本是5以上唷


2017年10月5日 星期四

[Design Pattern] 觀察者模式 (Observer Pattern)

「當餐廳更新菜單時,有申請要收到更新通知的會員,就會收到第一手消息」
一對多就是觀察者模式的主軸概念
觀察者模式有二個角色
  1. 主題 - Subject
  2. 觀察者 - Observer
依上面的範例來看
Subject 就是餐廳,Observer 就是會員,餐廳更新的時後就發通知給會員
那我們現在再把這個概念想的完整一點

先從Subject的角度來看,Subject裡要有什麼樣的功能呢?

通知不是發給所有的會員,是只發給「有申請要收到更新通知的會員」
那是不是就要有「註冊申請收到更新通知」的功能給會員申請使用呢
就是 RegisterObserver (我想申請當一個觀察者)

當然有註冊也要有取消再收到通知的功能
就是 RemoveObserver (我覺得煩 不想再收到通知了)

那是不是還少了通知的功能呢
就是 NotifyObserver (通知會員囉)

從Observer的角度來看,Object裡要有什麼功能呢?

收到從餐廳發來的更新會送到哪裡呢?會員的信箱、電話、地址等等。
這麼多個方法可以收到餐廳的更新通知,那就統一一個名稱吧
就是 Update() (因為收到更新後會員對餐廳的菜單就會進行資料更新呀)

從實作的角度來看,我們該怎麼實作觀察者模式

首先不管什麼模式核心理念都是物件與物件之間要鬆綁,不需要知道彼此的細節
鬆綁就是不要物件與物件之間直接呼叫,透過中間人來呼叫也就是介面

Java程式碼:
1. 建立觀察者的介面
public interface ClientObserver {
  public void update();
}

2. 建立主題的介面
public interface NewsPaperSubject {
  public void registerObserver(ClientObserver o);
  public void removeObserver(ClientObserver o);
  public void notifyObserver();
}

3. 建立主體物件
public class NewsPaper implements NewsPaperSubject {
  ArrayList clients = new ArrayList<>(); // storage observer list

  @Override
  public void registerObserver(ClientObserver o){
    clients.add(o);
  }

  @Override
  public void removeObserver(ClientObserver o){
    int i = clients.indexOf(o);
    if (i >=1) {
      clients.remove(i);
    }
  }

  @Override
  public void notifyObserver(){
     for(ClientObserver client : clients){
       client.update();
     }
  }
}

4. 建立觀察者物件
public class ATeamObserver implement ClientObserver{
  NewspaperSubject subject;

  public ATeamObserver(NewspaperSubject newsPaperSubject){
    subject = newsPaperSubject;
    subject.registerObserver(this);
  }

  @Override
  public void update(){
    //TODO: update data
  }

  public void cancelObserver()(
    subject.removeObserver();
  }
}

如果有新的觀察者加入不需要修改既有的程式碼
只需要再建一個新的觀察者物件並實作ClientObserver

只要有register加入newsPaperSubject的觀察者,當newsPaperSubject呼叫notifyObserver
都會收到通知進入update(),就可以各自處理收到通知後的行為
這種一對多即時更新的模式,就適用Observer Pattern

2017年10月2日 星期一

設計模式 Design Pattern

程式設計師就像是一位翻譯官,負責人類和3C產品之間的溝通交流
將所有現實中的行為邏輯翻譯成電腦看的懂的語言
而現實生活中的語言表達,如果可以讓人清楚的明白意思,那就是個良好的表達
轉換到程式設計,如果可以讓人清楚的明白意思,那就是良好的程式碼

常有人會問寫程式難嗎?
我認為寫程式並不難,你可以用基本的if..else 條件判斷完成大部分的功能
只要有心人人都可以寫出一個程式,但要怎麼體現一個工程師的價值
就是他的程式碼的表達能力有多好
優秀的軟體工程師的程式碼,是可以讓其它工程師容易了解他的架構及功能
這就是所謂的易讀性,易讀性高的程式可以降低bug發生的機率和易維護修改

設計模式就像是一個表達的技巧,在對的時間用上對的技巧就可以達到事半功倍的成效
這些技巧都是技術高超的工程師集思廣義整理出來的
他們提取出認為可以提高程式的易讀性、修改性的程式架構
當然可以不一定要照著他們的方法做
除非你覺得你比他們威,如果沒有.. 那還是先照做一下吧XD

設計模式最為知名的應該就是GoF的23種模式
業界大多都照著GoF的模式開發
愈多人用表示這個模式的溝通性就愈高
所以我以GoF的模式以比較常見的幾種來學習模倣

  • 策略模式 (Strategy Pattern)
  • 觀察者模式 (Observer Pattern)
  • 裝飾者模式 (Decorator Pattern)
  • 工廠模式 (Factory Pattern)
  • 獨體模式 (Singleton Pattern)
  • 命令模式 (Command Pattern)
  • 轉接器和外觀模式 (Adapter Pattern and Facade Pattern)
  • 樣板方法模式 (Template Method Pattern)
  • 反覆器和合成模式 (Iterator and Composite Pattern)

之後會分享一下我的學習心得