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返100
fs = new CashRebateReturnFactory(1,300d,100d);
break;
case 5://先打8折,再满300返100
fs = 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返n
public 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=100
public 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.8
public 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