1. 程式人生 > >設計模式——抽象工廠 Java原始碼

設計模式——抽象工廠 Java原始碼

前言

本程式改編自《Head First Design Patterns》(簡稱HF )中的關於Pizza的例子(可以在GitHub下載)。

HF抽象工廠
圖:HF 上的抽象工廠類圖。Client位於圖右上角。AbstractProductA的一個子類和AbstractProductB的一個子類,兩個子類合在一起構成一個產品族,即 a family of products

HF 上面的例子的Client是一個抽象類,及其子類;然後又用了一個工廠方法模式,封裝了new Client( )的過程。我覺得這樣子沒有必要,於是把書上的例子做了簡化:把Client改成普通類;然後再用一個簡單工廠封裝new Client( )的過程。這樣子絲毫不影響抽象工廠的精髓,反而還可以簡化程式碼,突出重點。

PS. 實驗樓網站裡面的例子中,Client是一個普通類,沒有用簡單工廠或者工廠方法模式封裝Client。我覺得這樣子不好,因為這樣直接把”Client的new的過程“暴露在Main裡面了。

抽象工廠模式,可以和簡單工廠或者工廠方法模式“組合在一起”,讓後兩者進一步封裝抽象工廠中的Client。

嚴格的類圖

總共14個java檔案

  • 兩個抽象產品介面
  • 四個具體產品類
  • 一個抽象工廠介面
  • 四個具體工廠類
  • 一個Client類
  • 一個封裝Client類的簡單工廠
  • 一個Main類

抽象工廠類圖
圖:我繪製的類圖,嚴格的抽象工廠模式類圖(一些資料不區分聚合、關聯、依賴,把這三種關係統統當作依賴關係)。我使用了一個叫做AmaterasUML 的外掛。

Talk is cheap, show me the code

兩個抽象產品介面

package abstractFactory;

// 抽象產品A——蔬菜
public interface Veggies {

    public String toString();
}
package abstractFactory;

// 抽象產品B——肉餡
public interface Meat {

    public String toString();
}

四個具體產品類

package abstractFactory;

// 具體產品A1——白菜
public class Cabbage implements Veggies { public String toString() { return "白菜"; } }
package abstractFactory;

// 具體產品A2——青椒
public class GreenPepper implements Veggies 
{
    public String toString()
    {
        return "青椒";
    }
}
package abstractFactory;

// 具體產品B1——豬肉餡
public class Pork implements Meat {

    public String toString()
    {
        return "豬肉";
    }
}
package abstractFactory;

// 具體產品B2——牛肉餡
public class Beaf implements Meat 
{
    public String toString()
    {
        return "牛肉";
    }
}

一個抽象工廠介面

package abstractFactory;

// 工廠介面
public interface BaoZiIngredientFactory
{
    public abstract Veggies createVeggies();   // 蔬菜類產品
    public abstract Meat    createMeat();      // 肉類產品  
}

四個具體工廠類

package abstractFactory;

// 具體工廠1生產的白菜豬肉包子
public class BaoZiIngredientFactory1 implements BaoZiIngredientFactory
{

    @Override
    public Veggies createVeggies()
    {
        return new Cabbage(); // A1
    }

    @Override
    public Meat createMeat()
    {
        return new Pork();    // B1
    }

}
package abstractFactory;

// 具體工廠2生產的白菜牛肉包子
public class BaoZiIngredientFactory2 implements BaoZiIngredientFactory
{

    @Override
    public Veggies createVeggies()
    {
        return new Cabbage(); // A1
    }

    @Override
    public Meat createMeat()
    {
        return new Beaf();    // B2 
    }

}
package abstractFactory;

// 具體工廠3生產的青椒牛肉包子
public class BaoZiIngredientFactory3 implements BaoZiIngredientFactory
{

    @Override
    public Veggies createVeggies()
    {
        return new GreenPepper(); // A2
    }

    @Override
    public Meat createMeat()
    {
        return new Beaf();        // B2
    }

}
package abstractFactory;

//具體工廠4生產的青椒豬肉包子
public class BaoZiIngredientFactory4 implements BaoZiIngredientFactory
{

    @Override
    public Veggies createVeggies()
    {
        return new GreenPepper();  // A2
    }

    @Override
    public Meat createMeat()
    {
        return new Pork();         // B1
    }

}

一個Client

package abstractFactory;

// Client即包子,聚合兩個抽象產品,關聯工廠介面
public class BaoZi
{
    String name;

    // 一個包子就是一個"a family of products"
    // 一個包子由分佈在兩類抽象產品中的三個具體產品構成
    Veggies veggies; // A類產品-蔬菜餡
    Meat    meat;    // B類產品-肉餡

    BaoZiIngredientFactory factory; // 工廠介面

    public BaoZi()
    {
        // 建構函式都不做什麼事
    }

    public void setIngredientFactory(BaoZiIngredientFactory factory)
    {
        this.factory = factory;
    }

    public void makeBaoZi()
    {
        prepare();  // 準備原材料
        steam();    // 蒸30分鐘
        setName(veggies.toString() + meat.toString() + "包子");
    }

    void prepare()
    {
        veggies = factory.createVeggies();
        meat    = factory.createMeat();
        System.out.println("Preparing " + veggies.toString() + "," + meat.toString());
    }

    void steam()
    {
        System.out.println("Steam for 30 minutes");
    }

    void setName(String name)
    {
        this.name = name;
    }

    String getName()
    {
        return name;
    }

    public String toString()
    {
        StringBuffer result = new StringBuffer();
        result.append("---- " + name + " ----\n");
        if (veggies != null)
        {
            result.append(veggies);
            result.append("餡 + ");
        }
        if (meat != null)
        {
            result.append(meat);
            result.append("餡\n");
        }
        return result.toString();
    }
}

一個封裝Client類的簡單工廠

抽象工廠可以結合簡單工廠,當然也可以結合工廠方法模式,用來封裝new Client( ) 的過程。簡單工廠的本質是:依據傳入的引數,決定生產,然後return哪一個new BaoZi(產品族, a family of products)

package abstractFactory;

public class BaoZiStore
{
    protected  BaoZi createBaoZi(String baoZiName)
    {
        BaoZi baoZi = new BaoZi();
        BaoZiIngredientFactory factory = null;

        if(baoZiName.equals("白菜豬肉"))
        {
            factory = new BaoZiIngredientFactory1();
            baoZi.setIngredientFactory(factory);
        }
        else if(baoZiName.equals("白菜牛肉"))
        {
            factory = new BaoZiIngredientFactory2();
            baoZi.setIngredientFactory(factory);
        }
        else if(baoZiName.equals("青椒牛肉"))
        {
            factory = new BaoZiIngredientFactory3();
            baoZi.setIngredientFactory(factory);
        }
        else if(baoZiName.equals("青椒豬肉"))
        {
            factory = new BaoZiIngredientFactory4();
            baoZi.setIngredientFactory(factory);
        }

        return baoZi;
    }

    public BaoZi orderBaoZi(String baoZiName)
    {
        BaoZi baoZi = createBaoZi(baoZiName);
        baoZi.makeBaoZi();
        return baoZi;
    }
}

一個Main類

package abstractFactory;

public class Main
{

    public static void main(String[] args)
    {
        BaoZiStore baoZiStore = new BaoZiStore();
        BaoZi baoZi = null;

        baoZi = baoZiStore.orderBaoZi("白菜豬肉");
        System.out.println(baoZi);

        baoZi = baoZiStore.orderBaoZi("白菜牛肉");
        System.out.println(baoZi);

        baoZi = baoZiStore.orderBaoZi("青椒牛肉");
        System.out.println(baoZi);

        baoZi = baoZiStore.orderBaoZi("青椒豬肉");
        System.out.println(baoZi);

    }   

}

執行結果

直接從eclipse複製過來

Preparing 白菜,豬肉
Steam for 30 minutes
---- 白菜豬肉包子 ----
白菜餡 + 豬肉餡

Preparing 白菜,牛肉
Steam for 30 minutes
---- 白菜牛肉包子 ----
白菜餡 + 牛肉餡

Preparing 青椒,牛肉
Steam for 30 minutes
---- 青椒牛肉包子 ----
青椒餡 + 牛肉餡

Preparing 青椒,豬肉
Steam for 30 minutes
---- 青椒豬肉包子 ----
青椒餡 + 豬肉餡

抽象工廠模式和工廠方法模式的差別

程式碼層面:
工廠方法模式:工廠抽象類,幾個工廠子類繼承這個抽象類,工廠子類new Product() ,任務完成。
抽象工廠模式:工廠介面,幾個工廠子類實現這個介面,工廠子類new 很多 Products(),但是任務還沒完。

我們需要的是”a family of Products”(產品族)。此時需要引入新的角色,Client。這個Client關聯工廠子類,把工廠子類new出來的所有Products聚合起來,形成一個產品族。然後再把這個產品族,看成”一個大產品“(我的程式碼中的包子)。為了讓這個”大產品(包子)的new的過程“不至於直接暴露在Main類中,把這個大產品封裝在一個簡單工廠裡面(或者HF 封裝在工廠方法模式裡面)。此時任務才完成~~

需求層面:
直接引用HF 裡面的原話,原話已經不能夠再簡練了。。。
Abstract Factory: Remember me, Abstract Factory, and use me whenever you have families of products you need to create and you want to make sure your clients create products that belong together.

Factory Method: And I’m Factory Method; use me to decouple your client code from the concrete classes you need to instantiate, or if you know ahead of time all the concrete classes you are going to need. To use me, just subclass me implement my factory method!