工廠模式的使用場景、讓自己的代碼解耦更優雅。包含簡單工廠、工廠方法、抽象工廠。一文就夠了 ...
關註公眾號 JavaStorm 獲取更多精彩
工廠模式定義
工廠方法(Factory Method)模式的意義是定義一個創建產品對象的工廠介面,將實際創建工作推遲到子類當中。核心工廠類不再負責產品的創建,這樣核心類成為一個抽象工廠角色,僅負責具體工廠子類必須實現的介面,這樣進一步抽象化的好處是使得工廠方法模式可以使系統在不修改具體工廠角色的情況下引進新的產品。
看下 GOF為工廠模式的定義:
“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”(在基類中定義創建對象的一個介面,讓子類決定實例化哪個類。工廠方法讓一個類的實例化延遲到子類中進行。)
工廠模式分類
- 簡單工廠模式 (Simple Factory),又稱靜態工廠方法模式 (Static Factory Method Pattern)。
- 工廠方法模式(Factory Method)。
- 抽象工廠模式(Abstract Factory)。
使用場景
- 生產一個發送消息的 產品對象,比如通過郵件、簡訊、微信公眾號等產品發送消息給用戶。
- Spring 中 FactoryBean 的 getObject();spring 中 各種各樣的 bean。就可以通過工廠模式創建並且實現了依賴解耦。
工廠模式的優點
- 解耦:把對象的創建和使用分開。
- 降低代碼複雜度:如果某個對象的創建比較複雜,或者其過程比較多的步驟。多個地方都會使用就會產生很多重覆代碼
- 降低維護成本:創建過程有工廠統一管理,當業務發生變化,不需要去找代碼中創建對象 A 的地方組個修改,只要在工廠裡面修改即可。開閉原則。
簡單工廠模式
其實這個並不算設計模式,適合創建簡單對象,創建的對象較少。客戶端不關心對象的創建過程。
簡單工廠模式角色
- 工廠角色(Factory):簡單工廠模式的核心,負責創建所有實例的內部邏輯,提供外部使用創建所需要的產品。
- 抽象產品角色(Product):簡單工廠所創建的類型。
- 具體產品(Concrete Product)角色:簡單工廠模式的創建目標,所有創建的對象都是充當這個角色的某個具體類的實例。
簡單工廠代碼實現
創建發送器介面,也就是產品角色
public interface Sender {
/**
* 發送信息
* @param to 收件人
* @param msg 消息
* @return
*/
boolean send(String to, String msg);
}
創建具體產品,分別是郵件發送器、簡訊發送器。
public class MailSender implements Sender {
@Override
public boolean send(String to, String msg) {
System.out.println("MailSender:收件人:" + to + ",消息為:" + msg);
return true;
}
}
public class SmsSender implements SenderService {
@Override
public boolean send(String to, String msg) {
System.out.println("SmsSender:收件人:" + to + ",消息為:" + msg);
return true;
}
}
創建簡單工廠
/**
* 工廠類,創建實例對象。缺點當字元串輸錯則得不到對象
* @author unique
*
*/
public class SendFactory {
public Sender getObject(String type) {
Sender sender = null;
switch (type) {
case "mail":
senderService = new MailSender();
break;
case "sms":
senderService = new SmsSender();
break;
default:
System.out.println("請輸入正確類型");
break;
}
return sender;
}
}
單元測試
public class SimpleFactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.getObject("mail");
sender.send("大兄弟", "你是最棒的!");
}
}
缺點
當我們新增產品類的時候,就需要修改工廠類中的 getObject() 方法,不符合 開放-封閉原則。
工廠方法模式
工廠模式中使用最多的一種。
與簡單公差個模式最大的區別就是我們不再提供一個統一的工廠來創建所有的產品,二十針對不同的產品提供不同的工廠。也就是每個產品都有一個與之對應的工廠。
適用場景
- 一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。
- 一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的介面,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏原則。
- 創建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無需關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或資料庫中。
工廠方法模式角色
- 抽象工廠(Abstract Factory)角色:是工廠方法模式的核心,與應用程式無關。任何在模式中創建的對象的工廠類必須實現這個介面。
- 具體工廠(Concrete Factory)角色 :這是實現抽象工廠介面的具體工廠類,包含與應用程式密切相關的邏輯,並且受到應用程式調用以創建某一種產品對象。
- 抽象產品(AbstractProduct)角色 :工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的介面。
- 具體產品(Concrete Product)角色 :這個角色實現了抽象產品角色所定義的介面。某具體產品有專門的具體工廠創建,它們之間往往一一對應
代碼示例
基於上面的簡單工廠,我們改造下。新建一個抽象工廠角色
public interface SenderFactory {
/**
* 生成對象
* @return
*/
public Sender getObject();
}
增加簡訊、郵件工廠類,實現抽象工廠介面。
public class SendMailFactory implements SenderFactory {
@Override
public Sender getObject() {
return new MailSender();
}
}
public class SendSmsFactory implements SenderFactory {
@Override
public Sender getObject() {
return new SmsSenderServiceImpl();
}
}
測試代碼
public class Test {
public static void main(String[] args) {
SenderFactory senderFactory = new SendMailFactory();
Sender sender = senderFactory.getObject();
sender.send("大兄弟", "你是最棒的!");
}
}
抽象工廠模式
在工廠方法模式中,其實我們有一個潛在意識的意識。那就是我們生產的都是同一類產品。抽象工廠模式是工廠方法的僅一步深化,在這個模式中的工廠類不單單可以創建一種產品,而是可以創建一組產品。這個產品會依賴多個合成一個。比如我們的電腦廠商 有因特爾和AMD 生產的主板與CPU。這個產品族有CPU跟主板。
抽象工廠模式和工廠方法模式一樣,都符合開放-封閉原則。但是不同的是,工廠方法模式在增加一個具體產品的時候,都要增加對應的工廠。但是抽象工廠模式只有在新增一個類型的具體產品時才需要新增工廠。也就是說,工廠方法模式的一個工廠只能創建一個具體產品。而抽象工廠模式的一個工廠可以創建屬於一類類型的多種具體產品。工廠創建產品的個數介於簡單工廠模式和工廠方法模式之間。
適用場景
- 和工廠方法一樣客戶端不需要知道它所創建的對象的類。
- 需要一組對象共同完成某種功能時,並且可能存在多組對象完成不同功能的情況。(同屬於同一個產品族的產品)
- 系統結構穩定,不會頻繁的增加對象。(因為一旦增加就需要修改原有代碼,不符合開閉原則)
抽象工廠方法模式的角色與工廠方法模式一致
我們的電腦有主板、CPU、記憶體…組成。這些都是產品,現在提供一個工廠,直接同時生產出 CPU與對應的主板,避免單獨去創建。也就是所謂的一套,一個系列。
比如 有因特爾工廠出品的 CPU 與 主板,AMD 工廠出品的 CPU 與主板。
代碼示例
定義CPU 與主板兩個產品
public interface CPU {
void calculate();
}
public interface Mainboard {
void installCPU();
}
以及產品的具體實現,比如 AMD 公司出品的、因特爾公司出品的 CPU 與主板
public class AmdCpu implements CPU {
/**
* CPU的針腳數
*/
private int pins = 0;
public AmdCpu(int pins) {
this.pins = pins;
}
@Override
public void calculate() {
System.out.println("AMD CPU的針腳數:" + pins);
}
}
public class IntelCPU implements CPU {
/**
* CPU的針腳數
*/
private int pins = 0;
public IntelCPU(int pins) {
this.pins = pins;
}
@Override
public void calculate() {
System.out.println("Intel CPU的針腳數:" + pins);
}
}
public class AmdMainboard implements Mainboard {
/**
* CPU插槽的孔數
*/
private int cpuHoles = 0;
/**
* 構造方法,傳入CPU插槽的孔數
*
* @param cpuHoles
*/
public AmdMainboard(int cpuHoles) {
this.cpuHoles = cpuHoles;
}
@Override
public void installCPU() {
// TODO Auto-generated method stub
System.out.println("AMD主板的CPU插槽孔數是:" + cpuHoles);
}
}
public class IntelMainboard implements Mainboard {
/**
* CPU插槽的孔數
*/
private int cpuHoles = 0;
/**
* 構造方法,傳入CPU插槽的孔數
*
* @param cpuHoles
*/
public IntelMainboard(int cpuHoles) {
this.cpuHoles = cpuHoles;
}
@Override
public void installCPU() {
System.out.println("Intel主板的CPU插槽孔數是:" + cpuHoles);
}
}
接著我們先創建一個抽象工廠角色,能生產 CPU 與主板系列產品的工廠方法定義。
public interface MainboardCPUFactory {
/**
* 創建CPU對象
*
* @return CPU對象
*/
public CPU createCpu();
/**
* 創建主板對象
*
* @return 主板對象
*/
public Mainboard createMainboard();
}
因特爾工廠定義
public class IntelFactory implements MainboardCPUFactory {
@Override
public CPU createCpu() {
return new IntelCPU(755);
}
@Override
public Mainboard createMainboard() {
return new IntelMainboard(755);
}
}
AMD 工廠
public class AmdFactory implements MainboardCPUFactory {
@Override
public CPU createCpu() {
return new AmdCpu(938);
}
@Override
public Mainboard createMainboard() {
return new AmdMainboard(938);
}
}
最後我們來測試
public class AbstactFactoryTest {
public static void main(String[] args) {
//使用因特爾工廠生產
MainboardCPUFactory intelFactory = new IntelFactory();
CPU cpu = intelFactory.createCpu();
Mainboard mainboard = intelFactory.createMainboard();
mainboard.installCPU();
cpu.calculate();
}
}
列印
Intel主板的CPU插槽孔數是:755
Intel CPU的針腳數:755
關註公眾號 JavaStorm 給你更多精彩。