1.工廠方法模式(Factory Method)定義
定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
1.1 UML圖:
主要有4個對象:
- 抽象工廠(Abstract Creator):提供一個創建產品的接口。調用者可以通過它訪問具體工廠的工廠方法。
- 具體工廠(Concrete Creator):繼承自抽象工廠,并實現其創建對象的方法。
- 抽象產品(Product):定義了產品的規范,描述了產品的主要特性和功能。
- 具體產品(Concrete Product):實現了抽象產品中所定義的接口,由具體工廠來創建,與同具體工廠之間是一一對應的。
2.工廠方法模式舉例:
業務場景:需要實現一個商場收銀系統,有四種策略,
- 正常結賬
- 打折
- 滿減
- 先打折再滿減
簡單工廠模式 -> 工廠方法模式:
簡單工廠模式UML圖:簡單工廠 + 策略模式 + 裝飾模式 具體實現邏輯,見裝飾模式 -> 2.裝飾模式舉例:
工廠方法模式UML:策略模式 + 裝飾模式 + 工廠方法模式
2.2 核心代碼:
ISale接口
public interface ISale {public double acceptCash(double price,int num);}
IFactory接口
public interface IFactory {public ISale createSalesModel(); //創建銷售模式}
CashContex:
public class CashContext {private ISale cs; //聲明一個ISale接口對象//通過構造方法,傳入具體的收費策略public CashContext(int cashType){IFactory fs=null;switch(cashType) {case 1://原價fs = new CashRebateReturnFactory(1d,0d,0d);break;case 2://打8折fs = new CashRebateReturnFactory(0.8d,0d,0d);break;case 3://打7折fs = new CashRebateReturnFactory(0.7d,0d,0d);break;case 4://滿300返100fs = new CashRebateReturnFactory(1,300d,100d);break;case 5://先打8折,再滿300返100fs = new CashRebateReturnFactory(0.8d,300d,100d);break;case 6://先滿200返50,再打7折fs = new CashReturnRebateFactory(0.7d,200d,50d);break;}this.cs = fs.createSalesModel();}public double getResult(double price,int num){//根據收費策略的不同,獲得計算結果return this.cs.acceptCash(price,num);}
}
CashRebateReturnFactory
public class CashRebateReturnFactory implements IFactory {private double moneyRebate = 1d;private double moneyCondition = 0d;private double moneyReturn = 0d;public CashRebateReturnFactory(double moneyRebate,double moneyCondition,double moneyReturn){this.moneyRebate=moneyRebate;this.moneyCondition=moneyCondition;this.moneyReturn=moneyReturn;}//先打x折,再滿m返npublic ISale createSalesModel(){CashNormal cn = new CashNormal();CashReturn cr1 = new CashReturn(this.moneyCondition,this.moneyReturn);CashRebate cr2 = new CashRebate(this.moneyRebate);cr1.decorate(cn); //用滿m返n算法包裝基本的原價算法cr2.decorate(cr1); //打x折算法裝飾滿m返n算法return cr2; //將包裝好的算法組合返回}
}
CashReturnRebateFactory
public class CashReturnRebateFactory implements IFactory {private double moneyRebate = 1d;private double moneyCondition = 0d;private double moneyReturn = 0d;public CashReturnRebateFactory(double moneyRebate,double moneyCondition,double moneyReturn){this.moneyRebate=moneyRebate;this.moneyCondition=moneyCondition;this.moneyReturn=moneyReturn;}//先滿m返n,再打x折public ISale createSalesModel(){CashNormal cn2 = new CashNormal();CashRebate cr3 = new CashRebate(this.moneyRebate);CashReturn cr4 = new CashReturn(this.moneyCondition,this.moneyReturn);cr3.decorate(cn2); //用打x折算法包裝基本的原價算法cr4.decorate(cr3); //滿m返n算法裝飾打x折算法return cr4; //將包裝好的算法組合返回}
}
CashSuper
public class CashSuper implements ISale {protected ISale component;//裝飾對象public void decorate(ISale component) {this.component=component;}public double acceptCash(double price,int num){double result = 0d;if (this.component != null){//若裝飾對象存在,則執行裝飾的算法運算result = this.component.acceptCash(price,num); }return result;}
}
CashReturn
public class CashReturn extends CashSuper {private double moneyCondition = 0d; //返利條件private double moneyReturn = 0d; //返利值//返利收費。初始化時需要輸入返利條件和返利值。//比如“滿300返100”,就是moneyCondition=300,moneyReturn=100public CashReturn(double moneyCondition,double moneyReturn){this.moneyCondition = moneyCondition;this.moneyReturn = moneyReturn;}//計算收費時,當達到返利條件,就原價減去返利值public double acceptCash(double price,int num){double result = price * num;if (moneyCondition>0 && result >= moneyCondition)result = result - Math.floor(result / moneyCondition) * moneyReturn; return super.acceptCash(result,1); }}
CashRebate
public class CashRebate extends CashSuper {private double moneyRebate = 1d;//打折收費。初始化時必需輸入折扣率。八折就輸入0.8public CashRebate(double moneyRebate){this.moneyRebate = moneyRebate;}//計算收費時需要在原價基礎上乘以折扣率public double acceptCash(double price,int num){double result = price * num * this.moneyRebate;return super.acceptCash(result,1);}}
CashNormal
public class CashNormal implements ISale {//正常收費,原價返回public double acceptCash(double price,int num){return price * num; }
}
DemoTest
public class Demotest {public static void main(String[] args){System.out.println("**********************************************");System.out.println("工廠方法模式");System.out.println();int discount = 0; //商品折扣模式double price = 0d; //商品單價int num = 0; //商品購買數量double totalPrices = 0d;//當前商品合計費用double total = 0d; //總計所有商品費用Scanner sc = new Scanner(System.in);do {System.out.println("商品折扣模式如下:");System.out.println("1.正常收費");System.out.println("2.打八折");System.out.println("3.打七折");System.out.println("4.滿300送100");System.out.println("5.先打8折,再滿300送100");System.out.println("6.先滿200送50,再打7折");System.out.println("請輸入商品折扣模式:");discount = Integer.parseInt(sc.nextLine());System.out.println("請輸入商品單價:");price = Double.parseDouble(sc.nextLine());System.out.println("請輸入商品數量:");num = Integer.parseInt(sc.nextLine());System.out.println();if (price>0 && num>0){//根據用戶輸入,將對應的策略對象作為參數傳入CashContext對象中CashContext cc = new CashContext(discount);//通過Context的getResult方法的調用,可以得到收取費用的結果//讓具體算法與客戶進行了隔離totalPrices = cc.getResult(price,num);total = total + totalPrices;System.out.println();System.out.println("單價:"+ price + "元 數量:"+ num +" 合計:"+ totalPrices +"元");System.out.println();System.out.println("總計:"+ total+"元");System.out.println();}}while(price>0 && num>0);System.out.println();System.out.println("**********************************************");}
}
輸出結果:
3. 工廠方法模式的優缺點;
- 優點:
- 可以避免創建者和具體產品之間的緊密耦合,針對工廠接口編程,而并非具體實現類。
- 單一職責原則。可以將產品創建代碼放在程序的單一位置,從而使得代碼更容易維護。
- 開閉原則。無需更改現有客戶端代碼,就可以在程序中引入新的產品類型。
- 缺點:
- 每增加一個產品就要增加一個具體產品類和一個對應的具體工廠類,增加了系統的復雜度。
4. 總結
- 通常我們把被創建的對象稱之為【產品】, 創建【產品】的對象稱為【工廠】。
- 當產品比較固定且數量少的情況下,只需要一個工廠類就可以,稱之為【簡單工廠】, 多個工廠時,就稱為工廠方法模式,其中工廠方法使一個類的實例化延遲到其子類,而簡單工廠實例化就是在唯一工廠類。
5.參考
- https://www.cnblogs.com/mklblog/p/18029716