在我們平常創建物件的時候,都是通過關鍵字 new 來實作的,例: Class A = new A() ,
在一些情況下,要創建的物件需要一系列復雜的初始化操作,比如查組態檔、查資料庫表、初始化成員物件等,如果把這些邏輯放在建構式中,會極大影響代碼的可讀性,不妨定義一個類來專門負責物件的創建,這樣的類就是工廠類,這種做法就是工廠模式,在任何需要生成復雜物件的地方,都可以使用工廠模式,
工廠模式包括:簡單工廠(不在23種設計模式中)、工廠方法和抽象工廠,
解決的問題
客戶端在呼叫時不想判斷來實體化哪一個類或者實體化的程序過于復雜,
在工廠模式中,具體的實作類創建程序對客戶端是透明的,客戶端不決定具體實體化哪一個類,而是交由“工廠”來實體化,
簡單工廠
? 結構
定義一個創建物件的介面,讓其子類自己決定實體化哪一個工廠類,
抽象類或介面:定義了要創建的產品物件的介面,
具體實作:具有統一父類的具體型別的產品,
產品工廠:負責創建產品物件,工廠模式同樣體現了開閉原則,將“創建具體的產品實作類”這部分變化的代碼從不變化的代碼“使用產品”中分離出來,之后想要新增產品時,只需要擴展工廠的實作即可,
? 使用
創建不同品牌的鍵盤
public interface Keyboard {
void print();
void input(Context context);
}
class HPKeyboard implements Keyboard {
@Override
public void print() {
//...輸出邏輯;
}
@Override
public void input(Context context) {
//...輸入邏輯;
}
}
class DellKeyboard implements Keyboard {
@Override
public void print() {
//...輸出邏輯;
}
@Override
public void input(Context context) {
//...輸入邏輯;
}
}
class LenovoKeyboard implements Keyboard {
@Override
public void print() {
//...輸出邏輯;
}
@Override
public void input(Context context) {
//...輸入邏輯;
}
}
/**
* 工廠
*/
public class KeyboardFactory {
public Keyboard getInstance(int brand) {
if(BrandEnum.HP.getCode() == brand){
return new HPKeyboard();
} else if(BrandEnum.LENOVO.getCode() == brand){
return new LenovoKeyboard();
} else if(BrandEnum.DELL.getCode() == brand){
return new DellKeyboard();
}
return null;
}
public static void main(String[] args) {
KeyboardFactory keyboardFactory = new KeyboardFactory();
Keyboard lenovoKeyboard = KeyboardFactory.getInstance(BrandEnum.LENOVO.getCode());
//...
}
}
? 缺陷
上面的工廠實作是一個具體的類KeyboardFactory,而非介面或者抽象類,getInstance()方法利用if-else創建并回傳具體的鍵盤實體,如果增加新的鍵盤子類,鍵盤工廠的創建方法中就要增加新的if-else,這種做法擴展性差,違背了開閉原則,也影響了可讀性,所以,這種方式使用在業務較簡單,工廠類不會經常更改的情況,
工廠方法
為了解決上面提到的"增加if-else"的問題,可以為每一個鍵盤子類建立一個對應的工廠子類,這些工廠子類實作同一個抽象工廠介面,這樣,創建不同品牌的鍵盤,只需要實作不同的工廠子類,當有新品牌加入時,新建具體工廠繼承抽象工廠,而不用修改任何一個類,
? 結構
抽象工廠:宣告了工廠方法的介面,
具體產品工廠:實作工廠方法的介面,負責創建產品物件,
產品抽象類或介面:定義工廠方法所創建的產品物件的介面,
具體產品實作:具有統一父類的具體型別的產品,
? 使用
public interface IKeyboardFactory {
Keyboard getInstance();
}
public class HPKeyboardFactory implements IKeyboardFactory {
@Override
public Keyboard getInstance(){
return new HPKeyboard();
}
}
public class LenovoFactory implements IKeyboardFactory {
@Override
public Keyboard getInstance(){
return new LenovoKeyboard();
}
}
public class DellKeyboardFactory implements IKeyboardFactory {
@Override
public Keyboard getInstance(){
return new DellKeyboard();
}
}
? 缺點
每一種品牌對應一個工廠子類,在創建具體鍵盤物件時,實體化不同的工廠子類,但是,如果業務涉及的子類越來越多,難道每一個子類都要對應一個工廠類嗎?這樣會使得系統中類的個數成倍增加,增加了代碼的復雜度,
抽象工廠
為了縮減工廠實作子類的數量,不必給每一個產品分配一個工廠類,可以將產品進行分組,每組中的不同產品由同一個工廠類的不同方法來創建,
例如,鍵盤、主機這2種產品可以分到同一個分組——電腦,而不同品牌的電腦由不同的制造商工廠來創建,
類似這種把產品類分組,組內不同產品由同一工廠類的不同方法實作的設計模式,就是抽象工廠模式,
抽象工廠適用于以下情況:
1. 一個系統要獨立于它的產品的創建、組合和表示時;
2. 一個系統要由多個產品系列中的一個來配置時;
3. 要強調一系列相關的產品物件的設計以便進行聯合使用時;
4. 當你提供一個產品類別庫,而只想顯示它們的介面而不是實作時;
? 結構
抽象工廠:宣告了創建抽象產品物件的操作介面,
具體產品工廠:實作了抽象工廠的介面,負責創建產品物件,
產品抽象類或介面:定義一類產品物件的介面,
具體產品實作:定義一個將被相應具體工廠創建的產品物件,
? 使用
public interface Keyboard {
void print();
}
public class DellKeyboard implements Keyboard {
@Override
public void print() {
//...dell...dell;
}
}
public class HPKeyboard implements Keyboard {
@Override
public void print() {
//...HP...HP;
}
}
public interface Monitor {
void play();
}
public class DellMonitor implements Monitor {
@Override
public void play() {
//...dell...dell;
}
}
public class HPMonitor implements Monitor {
@Override
public void play() {
//...HP...HP;
}
}
public interface MainFrame {
void run();
}
public class DellMainFrame implements MainFrame {
@Override
public void run() {
//...dell...dell;
}
}
public class HPMainFrame implements MainFrame {
@Override
public void run() {
//...HP...HP;
}
}
//工廠類,工廠分為Dell工廠和HP工廠,各自負責品牌內產品的創建
public interface IFactory {
MainFrame createMainFrame();
Monitor createMainFrame();
Keyboard createKeyboard();
}
public class DellFactory implements IFactory {
@Override
public MainFrame createMainFrame(){
MainFrame mainFrame = new DellMainFrame();
//...造一個Dell主機;
return mainFrame;
}
@Override
public Monitor createMonitor(){
Monitor monitor = new DellMonitor();
//...造一個Dell顯示幕;
return monitor;
}
@Override
public Keyboard createKeyboard(){
Keyboard keyboard = new DellKeyboard();
//...造一個Dell鍵盤;
return Keyboard;
}
}
public class HPFactory implements IFactory {
@Override
public MainFrame createMainFrame(){
MainFrame mainFrame = new HPMainFrame();
//...造一個HP主機;
return mainFrame;
}
@Override
public Monitor createMonitor(){
Monitor monitor = new HPMonitor();
//...造一個HP顯示幕;
return monitor;
}
@Override
public Keyboard createKeyboard(){
Keyboard keyboard = new HPKeyboard();
//...造一個HP鍵盤;
return Keyboard;
}
}
//客戶端代碼,實體化不同的工廠子類,可以通過不同的創建方法創建不同的產品
public class Main {
public static void main(String[] args) {
IFactory dellFactory = new DellFactory();
IFactory HPFactory = new HPFactory();
//創建戴爾鍵盤
Keyboard dellKeyboard = dellFactory.createKeyboard();
//...
}
}
? 優缺點
增加分組非常簡單,例如要增加Lenovo分組,只需創建Lenovo工廠和具體的產品實作類,分組中的產品擴展非常困難,要增加一個滑鼠Mouse,既要創建抽象的Mouse介面, 又要增加具體的實作:DellMouse、HPMouse, 還要再每個Factory中定義創建滑鼠的方法實作,
? 總結
簡單工廠:唯一工廠類,一個產品抽象類,工廠類的創建方法依據入參判斷并創建具體產品物件,
工廠方法:多個工廠類,一個產品抽象類,利用多型創建不同的產品物件,避免了大量的if-else判斷,
抽象工廠:多個工廠類,多個產品抽象類,產品子類分組,同一個工廠實作類創建同組中的不同產品,減少了工廠子類的數量,
在下述情況下可以考慮使用工廠模式:
在編碼時不能預見需要創建哪種類的實體,
系統不應依賴于產品類實體如何被創建、組合和表達的細節,
總之,工廠模式就是為了方便創建同一介面定義的具有復雜引數和初始化步驟的不同物件,工廠模式一般用來創建復雜物件,只需用new就可以創建成功的簡單物件,無需使用工廠模式,否則會增加系統的復雜度,
此外,如果物件的引數是不固定的,推薦使用Builder模式,
后記
在實際專案中,結合Spring中的InitializingBean介面,可以利用@Autowired注解優雅的實作工廠,
淘系技術部-行業技術團隊-誠招英才
我們是阿里巴巴淘系行業技術團隊,負責淘系行業電商業務的增長和創新,支撐著服飾、快消、家裝、消電等數十個行業快速發展,日均服務千萬用戶,產生千億級GMV,在這里,你可以接觸到整個淘系行業的核心業務和技術體系,賦能多個淘寶天貓行業,快速范訓創新業務;在這里,你還可以深入挖掘行業商業化和分銷場景,激活行業內各種生態角色,創造電商業務的新增量,
招聘崗位:java開發
如果您有興趣可發簡歷至weicleer.lwc@alibaba-inc.com,歡迎來撩~
? 拓展閱讀
作者|房輝(懷興)
編輯|橙子君
出品|阿里巴巴新零售淘系技術
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/232569.html
標籤:java
