天天看點

Android架構設計模式(二)——(抽象)工廠模式

一、介紹

所有的工廠類都是一個目的:降低具體産品與調用者(比如說用戶端)的耦合程度。對調用者隐藏産品的構造和變化(包括類名等)

舉一個實際的例子,來證明工廠模式的應用場景。

public class Product {
    public void template(){//模闆函數——不變
        //do something......
           hook_method();
    }
    public void hook_method();//卡榫函數——變
}
public class ProductA extends Product{
    public void hook_method(){
        //do something.....
    }
}
public class ProductB extends Product{
    public void hook_method(){
        //do something.....
    }
}
public class ProductC extends Product{
    public void hook_method(){
        //do something.....
    }
}
public class Client{
    public void main(String[] arg){
        Product proA = new ProductA();
        Product proB = new ProductB();
        Product proC = new ProductC();
        //.....如果說後面有一百個不同的Product,當然實際情況是,在不同的地方都會用到 new ProductA\B\C這樣的具體字眼。
        proA.template();
        proB.template();
        proC.template();
    }
}
           

考慮下面的問題:

1、如果說這個時候,需求變了,要你把産品的名字改了,OMGD。可以想象,你需要改多少個 new ProductX(( ▼-▼ ))。

2、更加實際的情況是,有些産品是需要量産和包裝的,它們的生産都是【标準化】的,是以如果構造這樣的對象較多的話,用戶端與具體的産品的耦合度就大大增加,同時,用戶端承擔的工作也過多。

使用工廠模式解決上面的困難

(1)簡單工廠模式

  • UML結構:
    Android架構設計模式(二)——(抽象)工廠模式
  • 設計思想:簡單工廠模式又叫靜态工廠模式,在工廠模式系列中屬于抽象層次最低的,也是最容易實作的。它是根據用戶端的要求(傳入的參數、調用的方法)來傳回所需要的對象,能夠很友善的實作用戶端與具體産品之間的解耦。
  • 優缺點:
    • 優點:結構簡單,調用者隻需要知道工廠和目标類别就可以。
    • 缺點:違反了開閉原則,如果添加産品類别的時候需要修改工廠代碼。
  • 代碼範例:

    模闆模式+簡單工廠

public class Product {
    public void template(){//模闆函數——不變
        //do something......
           hook_method();
    }
    public void hook_method();//卡榫函數——變
}
public class ProductA extends Product{
    public void hook_method(){
        //do something.....
    }
}
public class ProductB extends Product{
    public void hook_method(){
        //do something.....
    }
}
public class ProductC extends Product{
    public void hook_method(){
        //do something.....
    }
}
//簡單工廠+模闆模式
public class SimpleFactory{
   public static int TYPE_A = ;//産品類别
   public static int TYPE_B = ;
   public static int TYPE_C = ;
   public Product pd; // 産品的引用
   public Product createProduct(int type){
       switch(type){
          case TYPE_A:
          pd = new ProductA();
          break;
          case TYPE_B:
          pd = new ProductB();
          break;
          case TYPE_C:
          pd = new ProductC();
          break;
       }
       pd.function();//調用模闆初始化,包裝對象,對象的具體包裝留給具體類别去實作
       return pd;
   }
   public void function(){
       pd.template();  //模闆模式,隐藏了具體實作,解除了工廠與對象具體包裝的耦合
   }
}
public class Client{
    public void main(String[] arg){
      SimpleFactory fac = new SimpleFactory();
      Product proA = fac.createProduct(SimpleFactory.TYPE_A);
      Product proB = fac.createProduct(SimpleFactory.TYPE_B);
      Product proC = fac.createProduct(SimpleFactory.TYPE_C);
     //這個時候即使改變了産品的類名和實作,那麼用戶端也不需要知道,解除了用戶端和具體産品的耦合
     //但是增加産品的時候,需要修改工廠類,違反了開閉原則,容易出錯。
    }
} 
           

(2)工廠模式

  • UML結構:
    Android架構設計模式(二)——(抽象)工廠模式
  • 設計思想:把對象的建立延遲到子類實作,不同子類負責實作不同對象的建立,父類工廠負責将對象傳回給外部調用者。對外部隐藏了具體的類别;主要是針對類别單一的産品。
  • 優缺點:
    • 優點: 遵循開閉原則,如果添加産品類别則隻需要添加繼承類就可以,不需要修改工廠類的代碼。
    • 缺點: 用戶端需要知道具體的工廠名才能建立相應的對象;在某個産品有多個系列的時候,抽象工廠并不知道該建立哪個類别。
  • 代碼範例:
public class Product {
    public void template(){//模闆函數——不變
        //do something......
           hook_method();
    }
    public void hook_method();//卡榫函數——變
}
public class ProductA extends Product{
    public void hook_method(){
        //do something.....
    }
}
public class ProductB extends Product{
    public void hook_method(){
        //do something.....
    }
}
public class ProductC extends Product{
    public void hook_method(){
        //do something.....
    }
}
//父類工廠
public class ProductFactory{
    public abstract Product createProduct();   
}
//具體子類工廠+模闆模式
public class ProductAFactory{
    public Product createProduct(){
        Product pd = new ProductC();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
}
public class ProductBFactory{
    public Product createProduct(){
        Product pd = new ProductC();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
}
public class ProductCFactory{
    public Product createProduct(){
        Product pd = new ProductC();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }

}
public class Client{

      SimpleFactory facA = new ProductAFactory();
      SimpleFactory facB = new ProductBFactory();
      SimpleFactory facC = new ProductCFactory();

      //此處把具體産品的建立細節與用戶端解耦
      Product proA = facA.createProduct(SimpleFactory.TYPE_A);
      Product proB = facB.createProduct(SimpleFactory.TYPE_B);
      Product proC = facC.createProduct(SimpleFactory.TYPE_C);
     //這個時候即使改變了産品的類名和實作,那麼用戶端也不需要知道,解除了用戶端和具體産品的耦合
     //但是産品類别較多時,如果A、B、C下面各有兩個風格時,那麼工廠就不知道該建立哪一種了,這時候就需要用到抽象工廠模式了。
    }
} 
           

(3)抽象工廠模式

  • UML結構:
    Android架構設計模式(二)——(抽象)工廠模式
  • 設計思想:工廠方法模式針對的是一個産品等級結構;而抽象工廠模式針對的是多個産品等級結構。當産品有多個等級結構或者多個系列的時候,就需要用到抽象工廠模式,使得用戶端在不知道具體産品名的情況下建立具體的産品【不知而用】。
  • 優缺點:
    • 優點:在産品種類和系列比較多的情況下,行形成了一個産品等級結構時,可以将複雜的産品内部結構隐藏,簡化用戶端的操作。
    • 缺點: 當需要增加一個産品的時候,有多處需要添加新類;同時,如果對産品增加某個系列時需要修改原來的工廠類,違反開閉原則。
  • 代碼範例:

    假設有C、D兩個不同的産品,産品下面又分為兩種不同風格,比 如:StyleC1、StyleC2、StyleD1、StyleD2。

    這時候結構就需要改變了,抽象工廠有兩個模闆方法(createC、createD),負責生産StyleC和StyleD,但是抽象工廠本身是不知道是哪個類型的風格。

    具體風格的建立延遲給兩個子類工廠FactoryStyle1和FactoryStyle2負責。

//不同類别産品父類
public class ProductA extends Product{
    public void teplate(){ //模闆函數——不變
        //do something.....
        hook_method();
    }
    public void hook_method();//卡榫函數——變化
}
public class ProductB extends Product{
    public void teplate(){ //模闆函數——不變
        //do something.....
        hook_method();
    }
    public void hook_method();//卡榫函數——變化
}
//不同風格産品
public class ProductSytleA1 extends ProductA{
    public void hook_method(){
        //do something.....
    }
}
public class ProductStyleB1 extends ProductB{
    public void hook_method(){
        //do something.....
    }
}
public class ProductSytleA2 extends ProductA{
    public void hook_method(){
        //do something.....
    }
}
public class ProductStyleB2 extends ProductB{
    public void hook_method(){
        //do something.....
    }
}
//抽象父類工廠
public class ProductFactory{
    public abstract ProductA createA(); 
    public abstract ProductB createB(); 
    public abstract ProductC createC(); 
}
//具體子類工廠+模闆模式
public class FactoryStyle1{
    public ProductA createA(){
        ProductA pd = new ProductStyleA1();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
    public ProductB createB(){
        ProductB pd = new ProductStyleB1();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
    public ProductC createC(){
        ProductC pd = new ProductStyleC1();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
}
public class FactoryStyle2{
    public ProductA createA(){
        ProductA pd = new ProductStyleA2();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
    public ProductB createB(){
        ProductB pd = new ProductStyleB2();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
    public ProductC createC(){
        ProductC pd = new ProductStyleC2();
        pd.template(); // 模闆方法,初始化和裝配對象
        return pd;
    }
}

//用戶端
public class Client{
    public void main(String[] arg){
      //生産Style1類型的産品
      Factory fac1 = new FactoryStyle1();
      ProductA proA = fac1.createA();//此時解除了具體産品與用戶端的耦合。
      ProductB proB1 = fac1.createB();

      //生産Style2類型産品
      Factory fac2 = new FactoryStyle2();
      proA = fac2.createA();//用戶端根本不知道此時風格已經變了。
      proB = fac2.createB();
      //如果需要更多的類型,則隻需要知道工廠就可以了
      //如果需要改變産品的構造和名字,也無需通知用戶端
      //抽象工廠模式類别層次結構複雜,是以适合在産品層次較深、類别較多的情況
    }
}
           

二、Android綜合運用範例

還是之前的Mp3播放器,在Android之中用戶端(Activity)與播放器(MP3Player)之間解耦就是用到了工廠模式。這時候Service就充當了Factory的角色。當然裡面還有Template模式和Observer模式。

UML圖示:

Android架構設計模式(二)——(抽象)工廠模式

源代碼

參見Android架構設計模式(一)——Template Method

分析

這裡面,Service除了是單獨運作在背景的獨立線程之外,它還是充當了抽象工廠的角色,具體工廠是我們自己繼承實作的mp3Service_Factory:

public class mp3Service_Factory extends Service {  
    private IBinder mBinder = null;  
    @Override public void onCreate() {
    //此處就生成了MP3Player對象
      mBinder = new mp3Player(getApplicationContext()); 
    }  
    //該函數就是Service與子類的卡榫函數
    @Override public IBinder onBind(Intent intent) {    
    return mBinder; //傳回對象
    }
} 
           

結合UML圖看,如果我們現在還有電影、下載下傳的需求,那麼我們又可以繼承IBinder類來實作不同産品,繼承Service實作不同的裝配工廠。然而用戶端呢,永遠就隻需要知道IBinder和具體工廠便足夠。

分解一下具體的步驟:

1、我們可以看到,用戶端調用startService()來啟動服務,随後就啟動了Service抽象類反向呼叫了mp3Service_Factory的onCreate()函數,誕生了mp3Player産品對象。然後執行bindService()函數,将mConnection這個回調類(CallBack)與Service綁定。

public void onCreate(Bundle icicle) {   super.onCreate(icicle);
//do initial
//開啟服務
startService(new Intent(this, mp3Service_Factory.class));   
//将用戶端與服務進行綁定,通過mConnection中的卡榫函數回調來傳回Service工廠所建立的MP3Player對象。
bindService(new Intent(Client.this,mp3Service_Factory.class), mConnection, Context.BIND_AUTO_CREATE); 
 } 
           

2、建立了對象之後,Service抽象類反向呼叫了mp3Factory子類的onBind()卡榫函數傳回了mp3Player對象。然後Service抽象類再用mConnection回調onServiceConnected()函數:

private ServiceConnection mConnection = new ServiceConnection() {       
   public void onServiceConnected(ComponentName className, IBinder ibinder)  {   
   ib = ibinder;   
   }         
   public void onServiceDisconnected(ComponentName className) {}      
   }; 
           

将 建立好的MP3Player對象傳回給用戶端的ib(隻是抽象IBinder,并沒有涉及具體類名)。

3、用戶端擷取到了mp3Player對象後,調用IBinder裡面的transact()函數反向呼叫到mp3Player的onTransact()函數,進而實作音樂的播放。

//卡榫函數,接通mp3Player與父類IBinder
@Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        if ( == code){
            this.play();
        }else if (  == code){
            this.stop();
        }
        return true;
    }
           

4、請留意,從始至終,用戶端隻知道IBinder對象而已,并不知道具體的mp3Player,也就是說如果這個時候mp3Player改變類名、構造等用戶端都不知道的,即使換成mp4Player用戶端也是不知道的,隻需要實作一個mp4的工廠就可以了。

三、總結

其實上面的onServiceConnect()回調還是Observer模式,這個模式實作了用戶端與背景服務的異步通信,可以讓背景服務在準備好的時候通知用戶端(在這裡即傳回mp3Player對象)。

總的來說,工廠模式是用來解決以下問題的:

(1) 對調用者隐藏具體的産品細節,調用者隻需要告訴工廠需要生産什麼樣(磨具)的産品,而不需要知道具體産品的細節(類名)。

(2) 包裝對象的構造過程,直接将産品傳回給調用者,減輕調用者的負擔。

(3) 至于用哪種工廠模式,就要依照具體項目的複雜程度了。簡單的,不需要拓展的直接用簡單工廠,單一的産品就用工廠模式,産品延伸鍊較長、類别較多則使用抽象工廠。