你還在看錯誤的抽象工廠模式實現案例?


昨天在搜抽象工廠模式時,發現有好幾篇博文講的實現方式和我所認知的有出入,而且還看到某某教程講的也是錯誤的還在搜索引擎排第一。

大家講的簡單工廠模式和工廠方法模式都是沒問題的,關鍵到抽象工廠模式就不對了。

先從簡單工廠模式和工廠方法模式復習一下,然后再看看錯的是咋講的。已經對抽象工廠模式爛熟於心的請忽略此文。

簡單工廠模式:

有個一公共的產品接口,定義一些方法,各個具體的產品類都實現這個接口,然后有一個專門生產產品類實例的類被稱作工廠類,專門為客戶端生產產品實例,這個工廠的內部實現就是使用switch或ifelse進行邏輯判斷實現的,根據客戶端傳遞的參數不同,來決定什么時候創建什么樣的具體產品,生產出不同的具體產品返回給客戶端。這樣,這個工廠類就集成了所有產品的創建邏輯,它變得無所不知,這也讓它變成了核心。但這也成為了他的缺點,因為它將所有的邏輯集中放在一個類里,當產品接口有了新的產品實現類,工廠類需要增加代碼,判斷在什么時候創建該產品,就需要加入新的判斷邏輯。

 ShoesFactory工廠

public class ShoesFactory{
    public static final String NIKE = "NIKE";
    public static final String ADIDAS = "ADIDAS";
    public static Shoes getShoes(String brand) {
        Shoes shoes = null;
        switch (brand) {
            case "NIKE":
                shoes = new NikeShoes();
                break;
            case "ADIDAS":
                shoes = new AdidasShoes();
                break;
            default:
                shoes = null;
        }
        return shoes;
    }
}

 測試類

public class SimpleFactoryTest{
    public void main(String arg[]){
        Shoes nikeShoes = ShoesFactory.getShoes(ShoesFactory.NIKE);
        nikeShoes.desc();
        Shoes adidasShoes = ShoesFactory.getShoes(ShoesFactory.ADIDAS);
        adidasShoes.desc();
    }
}

 

缺點:每增加一個產品,都要新寫一個類,然后在工廠類里增加分支,不符合java的開閉原則。

優點:對外屏蔽產品實例的創建,客戶端只負責“消費”,通過這種方式實現了對職責的分割。

工廠方法模式:

有一個公共的產品接口和一個公共的產品工廠接口,任何具體工廠必須實現這個工廠接口。將具體的產品的實例創建任務交給具體的工廠類,工廠的職責單一化,每個產品實例從公共的工廠類內抽離出來,降低了耦合度。這樣,當增加新的產品實例時,只需要增加相應的工廠類,符合開閉原則。

 
ShoesFactory
public interface ShoesFactory{
    Shoes getShoes();
}

 NikeShoesFactory 

public class NikeShoesFactory implements ShoesFactory{
    @Override
    public Shoes getShoes() {
        return new NikeShoes();
    }
}

 AdidasShoesFactory 

public class AdidasShoesFactory implements ShoesFactory{
    @Override
    public Shoes getShoes() {
        return new AdidasShoes();
    }
}

 

當增加李寧鞋子時只需增加LiningShoes使其實現Shoes接口,然后增加LiningShoesFactory工廠類實現ShoesFactory接口使其負責生產LiningShoes的實例。

缺點:沒增加一個具體產品都要增加相應的工廠類,增加代碼量。

優點:符合開閉原則,易於擴展。

抽象工廠模式:

當有多個類型的產品時,產品類型之間橫向發展,如增加褲子、上衣等產品。在工廠方法模式里,只有一個產品接口類鞋子,各種鞋子如耐克鞋、阿迪鞋實現鞋子接口,而工廠類只需要寫2個就行了,耐克鞋工廠、阿迪鞋工廠類,各個工廠類職責單一只生產對應的一種具體產品。

而此時若增加了新的產品類型如褲子,他的具體產品阿迪褲子、耐克褲子,這個時候工廠類怎么實現呢?還像工廠方法類那樣嗎?如果業務上要求單一工廠原則(一個工廠只生產一個類型的產品),繼續使用工廠方法沒有問題,但如果需要提供一些列產品顯然使用工廠方法模式是不行的。

此時隨着多個產品類型的增加,產品之間形成一個個的產品族(耐克鞋、阿迪鞋是一個產品族,耐克褲子、阿迪褲子是一個產品族),而不同品牌之間形成產品等級。

網文實現方式

當我在網上搜索【抽象工廠模式】時,發現有不少文章給出的實現方式是如下圖這樣的。

抽象工廠模式結構:

 

factory 包里放的是抽象工廠類和它的具體工廠類 PantsFactory 褲子工廠,ShoesFactory 鞋子工廠,而抽象工廠類里有兩個方法 getPants 和 getShoes,所以對於這兩個工廠來說,繼承抽象工廠后都會有一個空的方法實現返回null,因為具體的工廠只生產某一產品,即褲子廠只生產褲子,歸於鞋子是空實現。

 

代碼實現

抽象工廠類

public abstract class AbstractFactory2{
    public abstract Shoes getShows(String brand);
    public abstract Pants getPants(String brand);
}

 PantsFactory 褲子工廠

public class PantsFactory extends AbstractFactory2{
    //褲子工廠不能生產鞋子,空實現 返回null
    @Override
    public Shoes getShows(String brand) {
        return null;
    }

    @Override
    public Pants getPants(String brand) {
        Pants pants = null;
        switch (brand) {
            case "NIKE":
                pants = new NikePants();
                break;
            case "ADIDAS":
                pants = new AdidasPants();
                break;
            default:
                pants = null;
        }
        return pants;
    }
}

 

ShoesFactory 鞋子工廠
public class ShoesFactory extends AbstractFactory2{
    @Override
    public Shoes getShows(String brand) {
        Shoes shoes = null;
        switch (brand) {
            case "NIKE":
                shoes = new NikeShoes();
                break;
            case "ADIDAS":
                shoes = new AdidasShoes();
                break;
            default:
                shoes = null;
        }
        return shoes;
    }
    //鞋子工廠不能生產鞋子,空實現 返回null
    @Override
    public Pants getPants(String brand) {
        return null;
    }
}

 

FactoryProductor2工廠生產者,通過不同的產品名稱創建具體工廠實例:(這里貌似簡單工廠模式)
public class FactoryProductor2{
    public static final String SHOES = "SHOES";
    public static final String PANTS = "PANTS";

    public static AbstractFactory2 getFactory(String productType) {
        if (SHOES.equalsIgnoreCase(productType)) {
            return new ShoesFactory();
        } else if (PANTS.equalsIgnoreCase(productType)) {
            return new PantsFactory();
        }
        return null;
    }
}

 測試類

public class AbstractFactoryTest2{
    public static void main(String[] args) {
        AbstractFactory2 factory = FactoryProductor2.getFactory(FactoryProductor2.PANTS);
        Pants nikePants = factory.getPants("NIKE");
        Pants adidasPants = factory.getPants("ADIDAS");
        nikePants.desc();
        adidasPants.desc();

        factory = FactoryProductor2.getFactory(FactoryProductor2.SHOES);
        Shoes nikeShoes = factory.getShows("NIKE");
        Shoes adidasShows = factory.getShows("ADIDAS");
        nikeShoes.desc();
        adidasShows.desc();
    }
}

 

NikePants
AdidasPants
NikeShoes
AdidasShoes

 

分析正確性

大家看這樣的抽象工廠,每次創建產品都要知道產品的品牌,而且工廠類的另外一個方法被廢棄。

還美名其曰的說產品族難擴展,產品等級易擴展。那我們就來看看在這種方式下是怎么個難易法。

產品族難擴展: 當我們想增加上衣Coat產品時,按現在的方式擴展一下看看。首先我們需要新建接口Coat,新建實現類NikeCoat和AdidasCoat實現Coat接口。然后我們需要在抽象工廠類增加創建Coat產品的方法,同時修改現有的2個工廠完成Coat相關實現類的實例創建邏輯,其次我們需要在FactoryProductor2增加分支邏輯創建CoatFactory。

撇開FactoryProductor2類不說,總結下我們都做了什么:

  • 新增產品接口Cost
  • 修改抽象工廠,新增getCoat方法,修改原有工廠增加getCoat的空實現
  • 新增具體工廠CoatFactory

產品等級易擴展: 當我們想增加李寧品牌時,我們需要做什么?pants包下創建LiNingPants實現pants接口,shoes包下創建LiNingShoes實現Shoes接口,然后修改factory包下的ShoesFactory工廠和PantsFactory工廠,各自增加分支判斷創建李寧牌的鞋子和褲子。

總結下我們都做了什么:

  • 新增具體產品 LiNingPants 和 LiNingShoes

  • 修改現有具體工廠類 ShoesFactory 工廠和 PantsFactory 工廠,增加創建李寧工廠分支

這樣看來產品族擴展起來確實難,而且不符合開閉原則,但產品等級擴展起來卻也不容易,也不符合開閉原則。

大家發現沒有,現在的抽象工廠的具體工廠內部是怎么實現的?還是使用switch或ifesle分支,不論擴展產品族還是產品等級都需要改具體工廠類,增加分支,都不符合開閉原則。這是正確的抽象工廠??

NO!!

這樣實現抽象工廠模式的不僅僅是搜到的幾篇博文,而且還有*鳥教程,搜索排第一:)

 

正確的抽象工廠

看下這個圖,發現具體工廠的名字變了,分別為AdidasFactory和NikeFactory,可想而知,以品牌命名具體的工廠只創建本品牌下的所有產品。

代碼實現

抽象工廠類

public abstract class AbstractFactory{
    public abstract Shoes getShows();
    public abstract Pants getPants();
}

這樣,每一個具體的工廠類,實現具體方法時,每個方法都能生成對應的產品,方法沒有空實現。而且每一個具體工廠內部沒有判斷分支。

NikeFactory耐克工廠

public class NikeFactory extends AbstractFactory{
    @Override
    public Shoes getShows() {
        return new NikeShoes();
    }
    @Override
    public Pants getPants() {
        return new NikePants();
    }
}

 

AdidasFactory阿迪工廠

public class AdidasFactory extends AbstractFactory{
    @Override
    public Shoes getShows() {
        return new AdidasShoes();
    }
    @Override
    public Pants getPants() {
        return new AdidasPants();
    }
}

 

而對於工廠生產者來說,通過不同的品牌名稱創建對應的工廠實例,

public class FactoryProductor{
    public static final String NIKE = "NIKE";
    public static final String ADIDAS = "ADIDAS";

    public static AbstractFactory getFactory(String brand) {
        if (NIKE.equalsIgnoreCase(brand)) {
            return new NikeFactory();
        } else if (ADIDAS.equalsIgnoreCase(brand)) {
            return new AdidasFactory();
        }
        return null;
    }
}

 測試類

public class AbstractFactoryTest{
    public static void main(String[] args) {
        AbstractFactory nikeFactory = FactoryProductor.getFactory(FactoryProductor.NIKE);
        Pants nikePants = nikeFactory.getPants();
        Shoes nikeShows = nikeFactory.getShows();
        nikePants.desc();
        nikeShows.desc();

        AbstractFactory adidasFactory = FactoryProductor.getFactory(FactoryProductor.ADIDAS);
        Shoes adidasShows = adidasFactory.getShows();
        Pants adidasPants = adidasFactory.getPants();
        adidasPants.desc();
        adidasShows.desc();

    }
}
NikePants
NikeShoes
AdidasPants
AdidasShoes

分析

現在我們再來看一下,所謂的產品族難擴展,產品等級易擴展。

產品族難擴展: 當增加cost上衣產品時,首先需要新建包coat,新建接口Coat,新建實現類NikeCoat和AdidasCoat,然后在抽象工廠類新增抽象方法getCoat,ShoesFactory工廠和PantsFactory工廠分別實現該方法。

都做了什么:新建產品接口Coat,並創建實現類,抽象工廠新增抽象方法,原具體工廠類重寫新增的抽象方法。

產品等級易擴展: 當增加新的品牌時,比如增加李寧牌的鞋子和褲子,怎么擴展?首先創建LiningShoes和LiningPants類分別實現Shoes和Pants接口,然后新建LiningFactory工廠類使其繼承AbstractFactory抽象工廠類,重寫getShoes和getPants方法。

都做了什么:新增產品實現類,新增具體工廠類。

類圖

 

增加產品族

增加后,這個系統中有三個產品族,兩個產品等級

增加產品等級

增加后,這個系統中2個產品族,3個產品等級

 


作者:walkinger
鏈接:https://juejin.im/post/5d4b8de1e51d453b1f37eac4



免責聲明!

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



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