Java 設計模式之工廠方法模式與抽象工廠模式

木木匠發表於2018-11-09

Java 設計模式之工廠方法模式與抽象工廠模式

一、前期回顧

上一篇《Java設計模式之單例模式》詳細介紹了單例模式,介紹了單例模式的使用場景,優缺點,同時也寫了兩種常見的單例模式寫法,懶漢式單例模式和餓漢氏單例模式,當然,單例模式的寫法還有很多,比如,列舉單例模式,靜態內部類單例模式等。有興趣的可以自行查詢資料。本篇開始介紹單例模式的第二篇,工廠方法模式,以及工廠模式的升級版,抽象工廠模式。

二、工廠方法模式的定義與實踐

定義:Define an interface for creating an object,but let subclasses decide which class to instantiation.Factory Mehod lets a class defer instantiation to subclasses.翻譯過來就是,定義一個用於建立物件的介面,讓其子類決定例項化哪個類。工廠方法讓一個類的例項化延遲到其子類。

上面的定義說明好像很繞口,如果不理解沒關係,我們用個例子來說明這個定義:

//定義產品介面
public interface Iproduct {
    public void doSomething();
}
//產品實現類
public class ProductA implements Iproduct {
    @Override
    public void doSomething() {
        System.out.println("我是產品A,我可以搞事情");
    }
}
//定義工廠類介面
public interface IFactory {
    //建立產品工廠方法
    < T extends Iproduct> T creatProduct(Class<T> clz);
}
//工廠方法實現類
public class ProductFactory implements IFactory {
    @Override
    public <T extends Iproduct> T creatProduct(Class<T> clz) {
        Iproduct product=null;
        try {
            product= clz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}
//場景類
public class Client {
    public static void main(String[] args) {
        IFactory factory=new ProductFactory();
        Iproduct product= factory.creatProduct(ProductA.class);
        product.doSomething();
    }
}
複製程式碼

上述例子,實現了一個簡單的工廠方法模式,定義了一個工廠介面類,然後具體的工廠方法實現了建立物件的邏輯。看到這裡,有人肯定會問,這裡要new一個工廠類的例項,和我new一個具體的物件有什麼區別呢?反正都要自己new,幹嘛要搞一個工廠類這麼繞呢?對,沒錯。這裡是要new一個工廠類,但是上面的例子建立產品的物件是比較簡單,所以感覺不出來,如果ProductA的例項建立是一個非常複雜的過程,那麼這個時候我通過工廠方法模式建立物件的例項就很方便了。

肯定還有人問,我覺得上面這個方式還有點囉嗦,有沒有更簡便的方式實現工廠模式呢?答案是,有。我們接下來看看簡單工廠模式。簡單工廠方法模式就是上述工廠方法模式的簡單版本。我們只要把上述工廠類的介面去掉,然後把工廠實現類改為靜態類就可以實現簡單工廠方法模式了。程式碼是這樣的:

//這裡只去掉了介面,改為靜態方法
public class ProductFactory  {
    public static <T extends Iproduct> T creatProduct(Class<T> clz) {
        Iproduct product=null;
        try {
            product= clz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}

public class Client {
    public static void main(String[] args) {
        Iproduct product= ProductFactory.creatProduct(ProductA.class);
        product.doSomething();
    }
}
複製程式碼

這裡就不用自己去new 工廠類啦,程式碼也簡潔了很多。但是去掉了介面也就意味著後面的擴充套件是不友好的,所以違反了開閉原則。

三、抽象工廠模式的定義與實踐

定義:Provide an interface for creating families of related or dependent objects without specifying their concrete classes.翻譯: 提供一個建立相關或者相互依賴的物件的介面,並且無需指定他們的具體實現類。

這裡理解可能有點困難,為了便於理解,我們先引入兩個概念。

產品等級結構: 產品等級結構即產品的繼承結構,如一個抽象類是汽車,其子類就是各個品牌的汽車,比如賓士,寶馬汽車。汽車和賓士就構成了一個產品等級結構,抽象汽車是父類,而具體品牌的汽車是其子類。

產品族 : 在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品,如賓士工廠生產的賓士跑車,寶馬公司生產的跑車,賓士跑車位於賓士產品等級結構中,寶馬跑車位於寶馬產品等級結構中。

所以,汽車和品牌是一個產品等級結構,而每個品牌下面有多個產品線,也就是說賓士牌下面的汽車有多個產品族,比如跑車族,SUV族。賓士跑車和寶馬跑車是一個產品族,賓士SUV和寶馬SUV是一個產品族。所以上面定義中“相關或者相互依賴的物件”就是場景中包含,同一個等級結構中有不同的產品族。 ,我們來用一個實際場景來說明下。

假設,我需要一個汽車工廠,汽車可以生產跑車和SUV車型,那麼這個時候用之前的工廠方法模式就不滿足了,方法工廠模式只能滿足生產一個產品,現在需要生產兩個產品。所以我們在方法工廠模式的基礎上改造下來滿足這個場景。

//汽車介面
public interface ICar {
     void dirver();
}
//抽象汽車類
public abstract class AbstractProduct implements ICar{
    @Override
    public void dirver() {
        System.out.println("我是汽車,我可以開動");
    }

    /***
     * 展示不同車的亮點*/
    public abstract void showSpecial();
}
//寶馬SUV實現類
public class BmwSuv extends AbstractProduct {
    @Override
    public void showSpecial() {
        System.out.println("我是寶馬牌SUV,我的特點就是多功能用途");
    }
}
//寶馬跑車實現類
public class BmwProductRoadster extends AbstractProduct {
    @Override
    public void showSpecial() {
        System.out.println("我是寶馬跑車,我的特點就是跑的快");
    }
}
//賓士SUV
public class BenzSuv extends AbstractProduct {
    @Override
    public void showSpecial() {
        System.out.println("我是賓士牌SUV,我的特點就是多功能用途");
    }
}
//賓士跑車
public class BenzProductRoadster extends AbstractProduct {
    @Override
    public void showSpecial() {
        System.out.println("我是賓士跑車,我的特點就是跑的快");
    }
}

//抽象工廠類
public abstract class AbstractFactory {
    //建立跑車
    public abstract < T extends ICar> T createRoadster();
    //建立SUV
    public abstract < T extends ICar> T createSuv();
}
//賓士汽車工廠類
public class BenzFactory extends AbstractFactory {
    @Override
    public <T extends ICar> T createRoadster() {
        return (T) new BenzProductRoadster();
    }

    @Override
    public <T extends ICar> T createSuv() {
        return (T) new BenzSuv();
    }
}
//寶馬汽車工廠類
public class BmwFactory extends AbstractFactory {
    @Override
    public <T extends ICar> T createRoadster() {
        return (T) new BmwProductRoadster();
    }

    @Override
    public <T extends ICar> T createSuv() {
        return (T) new BmwSuv();
    }
}
//場景類
public class Client {
    public static void main(String[] args) {
        AbstractFactory bmwFactory=new BmwFactory();
        AbstractFactory benzFactory=new BenzFactory();
        AbstractProduct bmwRoadster= bmwFactory.createRoadster();
        AbstractProduct bmwSuv=bmwFactory.createSuv();
        AbstractProduct benzRoadster=benzFactory.createRoadster();
        AbstractProduct benzSuv=benzFactory.createSuv();
        bmwRoadster.showSpecial();
        bmwSuv.showSpecial();
        benzRoadster.showSpecial();
        benzSuv.showSpecial();
    }
}

複製程式碼

細心的讀者應該發現,我們的改造相對於工廠方法模式而言,只是抽象工廠模式新增了多一個建立方法。這也是抽象工廠模式和工廠方法模式最明顯的區別,抽象工廠模式可以滿足多型別,多業務的場景,比如汽車工廠可以生產多種型別汽車,就適用於抽象工廠模式。而普通工廠方法模式就是針對生產某一種物件而言比較適用。總結來說就是,抽象工廠模式適用於多產品族的情況,普通工廠方法模式適用於單產品族情況。

四、總結

上面介紹了工廠方法模式與抽象工廠模式的定義與實踐,同時也衍生了一個簡單工廠模式。我們先來總結下工廠模式的優點與缺點。

優點:

1.良好的封裝性,程式碼結構清晰,不用關注具體物件的建立過程,符合迪米特法則。

2.良好的擴充套件性,我們擴充套件了產品,只要新建對應的工廠類,實現自己的實現邏輯即可。

缺點:

1.對於抽象工廠模式,產品族的擴充套件比較困難,需要修改頂部抽象工廠類,這會導致所有實現類都要修改,不符合開閉原則。

2.對於簡單工廠模式,由於沒有介面和抽象父類的約束,從而也會導致擴充套件只能修改程式碼,不符合開閉原則。

最後我們列一個表格來總結下工廠方法模式,簡單工廠模式,抽象工廠模式的區別。

簡單工廠模式 工廠方法模式 抽象工廠模式
有無介面或抽象類
適用場景 一般不涉及擴充套件,單產品族 涉及單產品族,便於擴充套件 多產品族場景,可以擴充套件等級結構,不便於擴充套件產品族
實現複雜度 最簡單 簡單 一般

五、參考

《設計模式之禪》

設計模式(三)抽象工廠模式

六、推薦閱讀

Java設計模式之單例模式

JAVA設計模式之開篇

帶你走進java集合之ArrayList

帶你走進java集合之HashMap

Java鎖之ReentrantLock(一)

Java鎖之ReentrantLock(二)

Java鎖之ReentrantReadWriteLock

相關文章