java設計模式之工廠方法探究


簡單工廠 + 工廠方法 + 抽象工廠
      看了十幾篇博客,每篇基本上都能有個自己的解釋,我匯總這些內容,重新梳理整理了一番,以形成自己的理解。
       簡單工廠模式其實不算23種設計模式之一,它是一個非常簡化版本的工廠。
      本文類圖均參考:  http://www.cnblogs.com/zhangchenliang/p/3700820.html  
      這里只有一個工廠對象SimpleFactory,負責創建多個AbstractProduct類型具體產品實例。
public class SimpleFactory {
    public static void main(String[] args) {
        Car car = CarFactory.createCar("Audi");
        car.setBrand("Audi");
        //生產
        car.produce();
    }
}
abstract class Car{
    private String brand;
    private String series;//暫時用不到
    public abstract void produce();
    
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public void setSeries(String series) {
        this.series = series;
    }
    public String getBrand() {
        return brand;
    }
    public String getSeries() {
        return series;
    }
}
//具體產品
class Audi extends Car{
    public void produce(){
        System.out.println("produce : " + this.getBrand());
    }
}
class Bmw extends Car{
    public void produce(){
        System.out.println("produce : " + this.getBrand());
    }
}
//簡單工廠
class CarFactory {
    public static Car createCar(String car){
        Car c = null;
        if("Audi".equalsIgnoreCase(car))
            c = new Audi();
        else if("Bmw".equalsIgnoreCase(car))
            c = new Bmw();
        return c;
    }
}
      在這個案例中,我要生產一輛車,不再需要自己new一個車,而只需要傳入一個類型(品牌),根據類型來創建車。的確隱藏了實現細節。但是問題在於:
      使用者首先必須確定要create哪種車,然后傳入類型,然后在工廠里又if-else判斷了到底是哪種車, 違背了開閉原則,我需要增加對其他子類的支持,必須修改代碼,增加else判斷分支。 毫無疑問是有問題的,看起來這個if-else很多余,我既然已經告訴你我要奧迪車,你為什么內部自己還判斷了呢。當然,反射是可以避免if-else 的,但是要使用Class.forName() 必須要傳入類的完全限定名,這點在使用中是非常麻煩的。
      如何解決?
      既然我在工廠里面也要判斷哪種品牌,那為什么我不將判斷拿出來呢?對於奧迪車,我建立一個小工廠專門生產奧迪車,而奔馳、寶馬我也各自建一個工廠去生產。這樣,在客戶調用的時候,你只需要告訴我你准備用那個公司的生產車間,我就能生產這個品牌的汽車,這也就是工廠方法模式。
    工廠方法模式,為了避免if-else判斷,干脆再封裝一層工廠
     以此圖為例
      client持有AbstractFactory的引用,AbstractFactory可創建具體工廠對象ConcreteFactory1, ConcreteFactory1。
每個具體工廠可生產具體的產品 ConcreteProduct1,ConcreteProduct2。這兩個具體產品可抽象為AbstractProduct。因此用抽象父類接受具體子類對象。
public class FactoryMod {
    @Test
    public void test() {
        CarFactory factory = new AudiFactory();
        factory.createCar().produce();
    }
    abstract class Car{
        private String brand;
        private String series;//暫時用不到
        public abstract void produce();
        public void setBrand(String brand) {
            this.brand = brand;
        }
        public void setSeries(String series) {
            this.series = series;
        }
        public String getBrand() {
            return brand;
        }
        public String getSeries() {
            return series;
        }
    }
    class Audi extends Car{
        public void produce(){
            System.out.println("produce : " + this.getBrand());
        }
    }
    class Bmw extends Car{
        public void produce(){
            System.out.println("produce : " + this.getBrand());
        }
    }
    class AudiFactory implements CarFactory{
        public  Car createCar(){
            Car audi = new Audi();
            audi.setBrand("audi");
            return audi;
        }
    }
    class BmwFactory implements CarFactory{
        public  Car createCar(){
            Car bmw = new Bmw();
            bmw.setBrand("bmw");
            return bmw;
        }
    }
    //工廠
    interface CarFactory {
        Car createCar();
    }
}
 
      此處我建了兩個工廠,分別生產不同品牌的汽車,這樣客戶端調用時,只要指明了是哪個工廠,就不必再有if-else判斷。如果我們增加奔馳生產的功能,只需要在Car下面增加奔馳類,並增加生產奔馳類的車間,無需改動代碼,就可以生成奔馳車。
而且我還可以指定生產什么型號的汽車:
class AudiFactory implements CarFactory{
        public  Car createCar(String series){
            Car audi = new Audi();
            audi.setBrand("audi");
            audi.setSeries(series);
            return audi;
        }
    }
    class BmwFactory implements CarFactory{
        public  Car createCar(String series){
            Car bmw = new Bmw();
            bmw.setBrand("bmw");
            bmw.setSeries(series);
            return bmw;
        }
    }
    //我指定汽車型號,客戶端想要生產汽車的時候必須告訴我型號,然后我就可以給你相應型號的汽車。
    interface CarFactory {
        Car createCar(String series);
    }
      上面的代碼就可以實現生產多個車型
      但是這種寫法其實是有問題的,或者說適用性不強。因為這里我是吧車型當成了Car的一個內部屬性,但是實際上,車型可能是一個單獨的屬於奧迪車的子類。
那么如果我不僅要生產奧迪,我還要指定車型,比如奧迪A4,奧迪A6呢?工廠方法如何實現?
public class FactoryMod2 {
    @Test
    public void test() {
        CarFactory factory = new AudiFactory();
        factory.createCar().produce();//生產奧迪汽車
        factory.createCar("A4").produce();//生產奧都A4
    }
    //汽車都有自己的序列號
    abstract class Car {
        private String id = String.valueOf(Math.random());
        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
        public abstract void produce();
    }
    //奧迪汽車還有自己的品牌名
    class Audi extends Car {
        private String brand = "Audi";
        public void produce() { 
System.out.println("produce : " + this.getBrand() + "," + this.getId());
}
public String getBrand() { return brand; } } //奧迪A4汽車除了品牌還有型號 class AudiA4 extends Audi { private String series = "A4"; public void produce() { System.out.println("produce : " + this.getBrand() + "," + this.getSeries() + "," + this.getId()); } public String getSeries() { return series; } } class AudiA6 extends Audi { private String series = "A6"; public void produce() { System.out.println("produce : " + this.getBrand() + "," + this.getSeries() + "," + this.getId()); } public String getSeries() { return series; } } class AudiFactory implements CarFactory { //要判斷客戶要什么系列 public Car createCar(String series) { Audi car = null; if (series.equals("A4")) return new AudiA4(); else if (series.equals("A6")) return new AudiA4(); return null; } public Car createCar(){ return new Audi(); } } //簡單工廠 interface CarFactory { Car createCar(String series); Car createCar(); } }
      代碼稍微復雜了一點,這里我省略了寶馬工廠的創建,都是相似的,並且調整了繼承體系。
      我們很容易在Audi下在派生子類AudiA4 和 AudiA6 沒問題,在用戶調用的時候我們要把用戶要求的型號告訴車間,車間里判斷用戶到底是要哪個系列,我們就給他哪個系列。很好,我們實現了功能:
produce :Audi,0.5005191840367693
produce :Audi,A4,0.1326089233983656
      但是if-else的問題又來了,我們在奧迪生產車間里,又一次遇到了if判斷,判斷到底是哪種系列。就像一開始我們用簡單工廠模式一樣。
隨着需求的復雜度提高,單一的奧迪工廠已經無法輕松寫意的生產各種型號的奧迪汽車,它的produce方法開始負責A4和A6汽車的生產, 這違背了單一職責原則。進而違背了開閉原則。
      怎么辦?
      我們還是從剛才簡單工廠到工廠方法的轉變上,試着尋求消除if-else的方式, 前面在簡單工廠中,調用者已經指明我要的是奧迪汽車,但是工廠里還是判斷了一次,因為工廠意圖承擔多個創建責任。我們分析之后覺得應該把這個判斷拿出來。也就是說讓客戶直接選擇要創建哪種汽車,但是又不能直接new,所以我們就在原先工廠的地方再用一次工廠,將原先的職責划分開來,形成奧迪工廠和寶馬工廠,然后調用者直接拿奧迪工廠生產奧迪汽車, 現在因為需求變更,奧迪工廠不得不承擔多個生產責任,產生多個if-else。我們要干掉他,所以我們選擇在奧迪工廠上,仿照上面的做法,再用工廠划分出奧迪A4產品線,奧迪A6產品線。
      我初步試了一下。
class AudiA4Factory implements CarFactory{
        public Car createCar(){
            return new AudiA4();
        }
    }
    class AudiA6Factory implements CarFactory{
        public Car createCar(){
            return new AudiA6();
        }
    }
      發現這種基於垂直的繼承體系,最好的辦法就是直接使用父類工廠,直接創建子類,也就是說無論是奧迪還是奧迪A4,還是寶馬,都直接用對應的AudiA4Factory,AudiA6Factory來生產。既然客戶端知道確定的類型,就直接創建確定類型的工廠。當然這里看起來好像不太科學,如果垂直繼承體系很深,那么不同各層級的工廠有些不太清晰。關於這種垂直繼承體系,使用什么來創建比較好,此處不展開討論,或許直接創建子類的工廠,子類的子類的工廠更好, 如果要實現單一職責開閉原則的話,容易變化的就不能作為方法存在,比如生產各種型號的汽車,如果做成createAudiAt() createAudiA6()方法,就會出現之前的問題。
      我們還有個問題沒有解決:抽象工廠怎么用?
     抽象工廠的應用場景,不是類似於這種垂直的產品體系,因為這種垂直的體系就只有一個產品等級結構。
      產品族與產品等級結構如圖所示。
    
 
      產品等級結構就是一系列具有垂直繼承體系的產品。產品族就是一系列沒有繼承關系的產品集合,比如手槍和子彈,鼠標和鍵盤。 
 
      通過工廠方法與抽象工廠方法的區別來理解抽象工廠模式:
    
      對於工廠方法:此處抽象產品類就是Car,派生的具體產品類就是Audi,Bmw。此處抽象工廠類就是CarFactory。具體工廠類就是AudiFactory.
根據前面的分析,抽象產品類可以派生多個具體產品類。抽象工廠類可以派生多個具體工廠類,而具體工廠類只能創建一個具體產品實例。多了就要違背開閉。
      對於抽象工廠:是有多個抽象產品類的,也就是多個產品族,例子有:汽車、飛機、航母;槍與子彈,鼠標與鍵盤等。是不同繼承體系的產品。
多個抽象產品類可以抽象出多個具體產品類,比如雷蛇鼠標雷柏鼠標,鍵盤可能是雷蛇鍵盤雷柏鍵盤等。 每個抽象工廠,可以派生多個具體工廠類,而每個具體工廠類可以創建多個產品實例,意思就是每個工廠都能生產鼠標和鍵盤,但是卻有不同的工廠去生產不同的實例。
類圖如下:
      現在有抽象工廠類AbstractFactory,它可以創建幾個具體的工廠 ConcreteFactory1,ConcreteFactory2,具體的每個工廠都能生產具體的A產品和B產品,但是A,B產品並沒有繼承關系,它們是不同的產品等級體系,現在要增加一個產品族,只需要增加一個相應產品族的工廠和具體的產品,比如A3,B3。大師要增加一個新產品比如C,那么3個工廠都需要修改內容,以生產新的產品。
代碼:
//抽象產品(Bmw和Audi同理)  
abstract class BenzCar{  
    private String name;  
      
    public abstract void drive();  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  
//具體產品(Bmw和Audi同理)  
class BenzSportCar extends BenzCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BenzSportCar-----------------------");  
    }  
}  
class BenzBusinessCar extends BenzCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BenzBusinessCar-----------------------");  
    }  
}  
  
abstract class BmwCar{  
    private String name;  
      
    public abstract void drive();  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  
class BmwSportCar extends BmwCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BmwSportCar-----------------------");  
    }  
}  
class BmwBusinessCar extends BmwCar{  
    public void drive(){  
        System.out.println(this.getName()+"----BmwBusinessCar-----------------------");  
    }  
}  
  
abstract class AudiCar{  
    private String name;  
      
    public abstract void drive();  
      
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
}  
class AudiSportCar extends AudiCar{  
    public void drive(){  
        System.out.println(this.getName()+"----AudiSportCar-----------------------");  
    }  
}  
class AudiBusinessCar extends AudiCar{  
    public void drive(){  
        System.out.println(this.getName()+"----AudiBusinessCar-----------------------");  
    }  
}  
  
  
//抽象工廠  
abstract class Driver3{  
    public abstract BenzCar createBenzCar(String car) throws Exception;  
      
    public abstract BmwCar createBmwCar(String car) throws Exception;  
      
    public abstract AudiCar createAudiCar(String car) throws Exception;  
}  
//具體工廠  
class SportDriver extends Driver3{  
    public BenzCar createBenzCar(String car) throws Exception {  
        return new BenzSportCar();  
    }  
    public BmwCar createBmwCar(String car) throws Exception {  
        return new BmwSportCar();  
    }  
    public AudiCar createAudiCar(String car) throws Exception {  
        return new AudiSportCar();  
    }  
}  
class BusinessDriver extends Driver3{  
    public BenzCar createBenzCar(String car) throws Exception {  
        return new BenzBusinessCar();  
    }  
    public BmwCar createBmwCar(String car) throws Exception {  
        return new BmwBusinessCar();  
    }  
    public AudiCar createAudiCar(String car) throws Exception {  
        return new AudiBusinessCar();  
    }  
}  
  
//老板  
public class BossAbstractFactory {  
  
    public static void main(String[] args) throws Exception {  
          
        Driver3 d = new BusinessDriver();  
        AudiCar car = d.createAudiCar("");  
        car.drive();  
    }  
}  
      上面的代碼比較清楚的展示了抽象方法的工作原理。
      此處的抽象工廠是Driver3,具體工廠是BussinessDriver,SportDriver等。抽象產品是AudiCar,具體產品是AudiSportCar和AudiBussinessCar。
因此對於抽象工廠,支持多個產品族,要增加一個產品族很容易,只需要增加一個具體工廠,比如生產SUV型轎車。只需要實現一個SUVDriver,但是要增加一種汽車產品線,比如大眾。就必須在所有的產品族工廠里增加createVolkswagenCar, 如果用鼠標鍵盤舉例子的話,就是每個廠商(雷柏雷蛇賽睿)就是具體的工廠,是產品族。而每中產品,鼠標鍵盤就是產品等級體系。
    有不清楚的可以繼續參考:
 
總結:
1.為什么用工廠方法?
  概括起來大致有以下的說法:
  將對象的實例化歸集起來,避免一個類到處實例化對象,一旦需求變更導致霰彈式修改,如果以后要改,直接在工廠里改就行了。
  類的某些實例化需要有比較多的初始化設置,放到工廠里可以封裝代碼,而不至於到處都是重復的初始化代碼。
  從解耦的角度考慮,不應該硬編碼,其實Spring才是最大的工廠,管理所有的代碼,實現所有代碼的解耦。springIOC有什么好處,工廠差不多也有這些好處。
  將對象本身的職責,對象的創建創建邏輯,使用邏輯隔離開來。
  -------------------------------------------------
  也有認為工廠模式華而不實的。
  也有認為工廠是C++時代設計模式遺留到java中的產物,其實並不一定需要,java通過反射可以方便的拿到所需要的實例。
  不過普通程序員在java開發中廣泛運用spring框架,多數時候不太需要,而且對於javaWeb這種CRUD的項目來說,也不需要太多的設計模式。最后就是,許多人都沒有找到適合工廠模式的應用場景,不能為了模式而套模式,導致過度設計。工廠方法到底有沒有用,我還不能下定論,或許5年之后,我能理解這個模式更深層次的內涵。
 
2.何時使用工廠方法。
  個人淺薄地認為,只要有new的地方,都可以考慮一下是否使用工廠,對於簡單很少有變動的情況,可以考慮用簡單的工廠,畢竟結構要清洗一點,也沒有特別大的變動。對於產品等級體系發達的情況,優先用工廠模式。對於產品族發達,而產品等級體系固定的情況,用抽象工廠。有些時候這幾種工廠可以組合使用,目的其實都是一樣,遵循6個設計原則,實現高內聚低耦合。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM