DLink 815路由器棧溢位漏洞分析與復現
工廠模式包括:簡單工廠(不在23種設計模式中)、工廠方法和抽象工廠。
在一些情況下,要建立的物件需要一系列複雜的初始化操作,比如查配置檔案、查資料庫表、初始化成員物件等,如果把這些邏輯放在建構函式中,會極大影響程式碼的可讀性。不妨定義一個類來專門負責物件的建立,這樣的類就是工廠類,這種做法就是工廠模式,在任何需要生成複雜物件的地方,都可以使用工廠模式。解決的問題
客戶端在呼叫時不想判斷來例項化哪一個類或者例項化的過程過於複雜。
在工廠模式中,具體的實現類建立過程對客戶端是透明的,客戶端不決定具體例項化哪一個類,而是交由“工廠”來例項化。
簡單工廠
▐ 結構
定義一個建立物件的介面,讓其子類自己決定例項化哪一個工廠類。
- 抽象類或介面:定義了要建立的產品物件的介面。
- 具體實現:具有統一父類的具體型別的產品。
- 產品工廠:負責建立產品物件。工廠模式同樣體現了開閉原則,將“建立具體的產品實現類”這部分變化的程式碼從不變化的程式碼“使用產品”中分離出來,之後想要新增產品時,只需要擴充套件工廠的實現即可。
▐ 使用
建立不同品牌的鍵盤
public interface Keyboard { void print(); void input(Context context); } class HPKeyboard implements Keyboard { @Overridepublic 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就可以建立成功的簡單物件,無需使用工廠模式,否則會增加系統的複雜度。