別再處處 new 對象了,試試 3 大工廠模式,真香!!

你還在處處 new 對象嗎?java

單身狗:我沒對象,new 怎麼了?git

new 對象自己是沒問題的,但也不能所有 new 關鍵字走天下,其實有更好的方式,合適的時候能夠試試工廠模式,代碼會更優雅。程序員

什麼是工廠模式?

顧名思義,工廠模式中的 "工廠" 指的是建立對象的工廠,它提供了一種建立對象的最佳方式,也就是工廠模式。github

工廠模式的好處是這些對象不須要暴露自身的建立過程,統一由工廠模式進行建立和提供,隱藏了建立細節,避免了錯誤的建立對象的形式,也減小了重複建立冗餘代碼。面試

通常狀況下,工廠模式能夠細分爲三類:spring

  • 簡單工廠模式
  • 工廠方法模式
  • 抽象工廠模式

不過在設計模式權威書籍《設計模式:可複用面向對象軟件的基礎》一書中,簡單工廠模式只是工廠方法模式的一個特例而已。設計模式

因此,從權威的角度說,工廠模式只分爲: 工廠模式抽象工廠模式 兩大類。intellij-idea

但無論白貓黑貓,能抓老鼠的就是好貓,設計模式亦是如此,無論怎麼分類,這些模式都是程序員們歷年過往經驗的濃縮,都是值得學習和借鑑的。ide

因此,本文棧長從細分的角度帶你們來實戰下這三個工廠設計模式。工具

一、簡單工廠

好比 XX 公司是作支付的,公司有幾大類的客戶:電商商戶、銀行客戶、代理商……

建立這些客戶的時候咱們能夠用簡單工廠模式來實現看看。

新建客戶基類:

能夠把全部客戶公共的信息放到一個客戶基類中,好比:客戶名、客戶類型等,全部的客戶繼承這個抽象基類。

/**
 * 客戶
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public abstract class Customer {

    /**
     * 客戶名稱
     */
    private String name;

    /**
     * 客戶類型
     */
    private String type;

}

新建電商商戶類:

/**
 * 商戶
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@ToString(callSuper = true)
public class Merchant extends Customer {

    /**
     * 合同類型
     */
    private int contractType;

    /**
     * 結算週期(天)
     */
    private int settmentDays;

    public Merchant(String name, String type) {
        super(name, type);
    }
}

新建銀行客戶類:

/**
 * 銀行客戶
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@ToString(callSuper = true)
public class BankPartner extends Customer {

    /**
     * 銀行編碼
     */
    private String code;

    /**
     * 銀行地址
     */
    private String address;

    public BankPartner(String name, String type) {
        super(name, type);
    }
}

新建代理商類:

/**
 * 代理商
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@ToString(callSuper = true)
public class Agent extends Customer {

    /**
     * 代理週期
     */
    private int period;

    /**
     * 代理產品
     */
    private int[] products;

    public Agent(String name, String type) {
        super(name, type);
    }
}

新增簡單工廠類:

新建一個簡單工廠,提供一個公共靜態方法,根據不一樣的客戶類型建立不一樣的客戶。

/**
 * 客戶簡單工廠
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class CustomerFactory {

    private static Merchant createMerchant(String type, String name) {
        return new Merchant(type, name);
    }

    private static BankPartner createBankPartner(String type, String name) {
        return new BankPartner(type, name);
    }

    private static Agent createAgent(String type, String name) {
        return new Agent(type, name);
    }

    public static Customer create(String type, String name) {
        if ("M".equals(type)) {
            return createMerchant(type, name);
        } else if ("B".equals(type)) {
            return createBankPartner(type, name);
        } else if ("A".equals(type)) {
            return createAgent(type, name);
        }
        return null;
    }

}

新建測試類:

/**
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class Test {

    public static void main(String[] args) {
        Customer merchant = CustomerFactory.create("M", "Java技術棧商戶");
        System.out.println(merchant);

        Customer bankPartner = CustomerFactory.create("B", "Java技術棧銀行客戶");
        System.out.println(bankPartner);

        Customer agent = CustomerFactory.create("A", "Java技術棧代理商");
        System.out.println(agent);
    }

}

輸出結果:

本節教程全部實戰源碼已上傳到這個倉庫:

https://github.com/javastacks/javastack

能夠看出簡單工廠的使用很簡單,就是耦合性過高了。

第一,對象和基類之間是基於繼承的。

第二,工廠類耦合了不一樣對象的建立,若是對象類型不是固定或者常常變更的,就要頻繁修改工廠類,好比我如今要再加一種客戶,就必需要改動工廠類,不符開閉原則。

因此,簡單工廠只適用於固定類型對象的建立。

二、工廠方法

工廠方法就是爲某類產品提供一個工廠接口,而後爲每一個產品提供一個工廠實現類。

廢話少說,咱們將簡單工廠的示例用工廠方法再改造一下。

新建工廠方法接口:

/**
 * 工廠方法客戶接口
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public interface CustomerFactory {

    Customer create(String type, String name);

}

新建商戶工廠實現類:

/**
 * 商戶工廠
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class MerchantFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new Merchant(type, name);
    }

}

新建銀行客戶工廠實現類:

/**
 * 銀行客戶工廠
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class BankPartnerFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new BankPartner(type, name);
    }

}

新建代理商工廠實現類:

/**
 * 代理商工廠
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class AgentFactory implements CustomerFactory {

    @Override
    public Customer create(String type, String name) {
        return new Agent(type, name);
    }

}

新建測試類:

/**
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class Test {

    public static void main(String[] args) {
        System.out.println("------工廠模式-工廠方法------");

        CustomerFactory merchantFactory = new MerchantFactory();
        Customer merchant = merchantFactory.create("M", "Java技術棧商戶");
        System.out.println(merchant);

        CustomerFactory bankPartnerFactory = new BankPartnerFactory();
        Customer bankPartner = bankPartnerFactory.create("B", "Java技術棧銀行客戶");
        System.out.println(bankPartner);

        CustomerFactory agentFactory  = new AgentFactory();
        Customer agent = agentFactory.create("A", "Java技術棧代理商");
        System.out.println(agent);
    }

}

輸出結果:

本節教程全部實戰源碼已上傳到這個倉庫:

https://github.com/javastacks/javastack

能夠看出,工廠方法也是挺簡單易用的,耦合性問題也解決了,每增長一個產品就新增一個產品工廠實現類就好了,擴展性很是好。

但也有一個問題,若是產品很是多,那勢必會形成工廠實現類氾濫,另一種可怕的場景就是,若是涉及到工廠接口變動,工廠實現類的維護簡直就是一種惡夢。

三、抽象工廠

工廠方法中一個工廠只能建立一個對象,若是如今每次建立客戶的時候都須要同時建立一份客戶擴展資料,那就能夠考慮使用抽象工廠。

新建客戶擴展基類:

能夠把全部客戶公共的擴展信息放到一個客戶擴展基類中,好比:客戶曾用名、客戶擴展說明等,全部的客戶繼承這個擴展抽象基類。

/**
 * 客戶擴展
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@NoArgsConstructor
public abstract class CustomerExt {

    /**
     * 客戶曾用名
     */
    private String formerName;

    /**
     * 客戶擴展說明
     */
    private String note;

}

新建商戶擴展類:

/**
 * 商戶
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@ToString(callSuper = true)
public class MerchantExt extends CustomerExt {

    /**
     * 介紹人
     */
    private int introduceName;

    /**
     * 介紹人電話
     */
    private String introduceTel;

}

新建銀行客戶擴展類:

/**
 * 銀行客戶擴展
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@ToString(callSuper = true)
public class BankPartnerExt extends CustomerExt {

    /**
     * 分行個數
     */
    private int branchCount;

    /**
     * ATM個數
     */
    private int atmCount;

}

新建代理商擴展類:

/**
 * 商戶
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
@Data
@ToString(callSuper = true)
public class AgentExt extends CustomerExt {

    /**
     * 來源
     */
    private String source;

    /**
     * 資質
     */
    private String certification;

}

新建抽象工廠接口:

/**
 * 抽象工廠客戶接口
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public interface CustomerFactory {

    Customer createCustomer(String type, String name);

    CustomerExt createCustomerExt();

}

新建商戶工廠實現類:

/**
 * 商戶工廠
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class MerchantFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new Merchant(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new MerchantExt();
    }

}

新建銀行客戶工廠實現類:

/**
 * 銀行客戶工廠
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class BankPartnerFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new BankPartner(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new BankPartnerExt();
    }

}

新建代理商工廠實現類:

/**
 * 代理商工廠
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class AgentFactory implements CustomerFactory {

    @Override
    public Customer createCustomer(String type, String name) {
        return new Agent(type, name);
    }

    @Override
    public CustomerExt createCustomerExt() {
        return new AgentExt();
    }

}

新建測試類:

/**
 * @author: 棧長
 * @from: 公衆號Java技術棧
 */
public class Test {

    public static void main(String[] args) {
        System.out.println("------工廠模式-抽象工廠------");

        CustomerFactory merchantFactory = new MerchantFactory();
        Customer merchant = merchantFactory.createCustomer("M", "Java技術棧商戶");
        CustomerExt merchantExt = merchantFactory.createCustomerExt();
        System.out.println(merchant);
        System.out.println(merchantExt);

        CustomerFactory bankPartnerFactory = new BankPartnerFactory();
        Customer bankPartner = bankPartnerFactory.createCustomer("B", "Java技術棧銀行客戶");
        CustomerExt bankPartnerExt = bankPartnerFactory.createCustomerExt();
        System.out.println(bankPartner);
        System.out.println(bankPartnerExt);

        CustomerFactory agentFactory = new AgentFactory();
        Customer agent = agentFactory.createCustomer("A", "Java技術棧代理商");
        CustomerExt agentExt = agentFactory.createCustomerExt();
        System.out.println(agent);
        System.out.println(agentExt);
    }

}

輸出結果:

能夠看出,抽象工廠和工廠方法十分相似,只不過抽象工廠裏面只生產一個對象,而抽象工廠能夠生產多個對象。

抽象工廠缺點也很明顯,第一就是和工廠方法同樣工廠類很是多,第二就是擴展很是麻煩,好比我如今要爲每一個客戶類型再加一份客戶特殊資料,那全部涉及到抽象工廠的工廠類都要改,是否是要瘋了。。

總結

若是有多個屬於同一種類型的類,能夠考慮使用工廠模式,統一提供生成入口,能從必定程度上解耦,擴展方便,也不用再處處 new 對象了。

但話又說回來,從示例能夠看出,若是使用或者設計不當也會帶來維護上的工做量。

本節教程全部實戰源碼已上傳到這個倉庫:

https://github.com/javastacks/javastack

好了,今天的分享就到這裏了,後面棧長我會更新其餘設計模式的實戰文章,公衆號Java技術棧第一時間推送。Java技術棧《設計模式》系列文章陸續更新中,請你們持續關注哦!

最後,以爲個人文章對你用收穫的話,動動小手,給個在看、轉發,原創不易,棧長鬚要你的鼓勵。

版權申明:本文系公衆號 "Java技術棧" 原創,原創實屬不易,轉載、引用本文內容請註明出處,禁止抄襲、洗稿,請自重,尊重你們的勞動成果和知識產權,抄襲必究。

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2021最新版)

2.終於靠開源項目弄到 IntelliJ IDEA 激活碼了,真香!

3.阿里 Mock 工具正式開源,幹掉市面上全部 Mock 工具!

4.Spring Cloud 2020.0.0 正式發佈,全新顛覆性版本!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

以爲不錯,別忘了隨手點贊+轉發哦!

相關文章
相關標籤/搜索