释义
工厂方法也是工厂模式的一种,我们回忆第一节的简单工厂方法,在简单工厂方法中,我们将各种不同的操作具体划分为一个实体类。这样我们就可以减少对原本业务客户端的代码改动。当我们需要新增需求的时候,我们增加业务类,并在工厂类中添加case即可。
简单工厂模式的最大优点就是在工厂类中包含一定的逻辑判断,根据客户端的选择动态的实例化相关的类。但是,也正如上面所描述,如果需要新增或者修改需求,那么就要对工厂类进行改动。这就违背了设计模式中要遵循的规则之一:开放-封闭原则。再次使用前几节中的超市促销案例,我们将原价、满减、打折的计算方式各自封装了一个类,然后用工厂去决定实例化哪一个类。如果我们要添加一个满减和打折同时触发的活动,就需要再新增计算类,并增加工厂类中的case。
public class CashContext {
private ISale cs;
public CashContext(int cashType) {
switch (cashType) {
case 1:
cs = new CashNormal();
break;
case 2:
cs = new CashRebate(0.7);
break;
case 3:
cs = new CashRebate(0.8);
break;
case 4:
cs = new CashReturn(300, 100);
break;
case 5:
CashNormal cashNormal = new CashNormal();
CashReturn cashReturn = new CashReturn(300, 100);
CashRebate cashRebate = new CashRebate(0.8);
cashReturn.decorate(cashNormal);
cashRebate.decorate(cashReturn);
cs = cashRebate;
}
}
public double getResult(double price, int num) {
return cs.acceptCash(price, num);
}
}
上面是在使用装饰模式的时候增加的代码。可以看到,当增加一个先打折再满减的时候,尽管使用了装饰模式,避免了多余类的创建,但是我们在类似工厂的CashContext中增加了大量的逻辑,显得特别复杂。下面直接上具体改动措施,然后再进行详解。
public abstract class IFactory {
public abstract ISale createSalesModel();
}
public class CashRebateReturnFactory extends 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;
}
@Override
public ISale createSalesModel() {
CashNormal cashNormal = new CashNormal();
CashReturn cashReturn = new CashReturn(moneyCondition, moneyReturn);
CashRebate cashRebate = new CashRebate(moneyRebate);
cashReturn.decorate(cashNormal);
cashRebate.decorate(cashReturn);
return cashRebate;
}
}
public class CashReturnRebateFactory extends 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;
}
@Override
public ISale createSalesModel() {
CashNormal cashNormal = new CashNormal();
CashReturn cashReturn = new CashReturn(moneyCondition, moneyReturn);
CashRebate cashRebate = new CashRebate(moneyRebate);
cashRebate.decorate(cashNormal);
cashReturn.decorate(cashRebate);
return cashReturn;
}
}
先添加一个抽象工厂类IFactory,并使用CashRebateReturnFactory和CashReturnRebateFactory继承该工厂,里面有三个参数,注释已经详细标明含义,在createSalesModel中,正是原先case里面的逻辑。这里我们做一个计算方式分类,原价可以看作是满0减0的满减计算,并打10折(即不打折)。满减可以看作打10折后进行满减。这几种情况的结合,可以分为两类,先满减,或先打折。不过区别就是根据需要可能满0减0,或打10折。所以这里分为了两个计算类即可。原价的时候,折扣传1,两个满减参数各传0。
//修改前
public CashContext(int cashType) {
switch (cashType) {
case 1:
cs = new CashNormal();
break;
case 2:
cs = new CashRebate(0.7);
break;
case 3:
cs = new CashRebate(0.8);
break;
case 4:
cs = new CashReturn(300, 100);
break;
case 5:
CashNormal cashNormal = new CashNormal();
CashReturn cashReturn = new CashReturn(300, 100);
CashRebate cashRebate = new CashRebate(0.8);
cashReturn.decorate(cashNormal);
cashRebate.decorate(cashReturn);
cs = cashRebate;
}
}
//修改后
public class CashContext {
private ISale cs;
public CashContext(int cashType) {
IFactory iFactory = null;
switch (cashType) {
case 1:
iFactory = new CashRebateReturnFactory(1, 0, 0);
break;
case 2:
iFactory = new CashRebateReturnFactory(0.8, 0, 0);
break;
case 3:
iFactory = new CashRebateReturnFactory(1, 300, 100);
break;
case 4:
iFactory = new CashReturnRebateFactory(0.8, 300, 100);
}
cs = iFactory.createSalesModel();
}
public double getResult(double price, int num) {
return cs.acceptCash(price, num);
}
}
改动后,构造方法还是返回Isale类型实体。改动前,根据选择的case具体的生成相应的计算类。并在getResult通过策略模式的方式在工厂内部进行行为计算。看着像是多了一层工厂的封装,将复杂的逻辑封装进了IFactory的子类实现中,这也是工厂方法的一个特性。这也符合设计模式的另一个规则:依赖倒转原则,将工厂类抽象出一个接口。所有要生产具体类的工厂,就是先实现该接口,或者继承一个抽象类。
工厂方法本质是对工厂模式的又一种抽象,类似一维进化成了二维,可扩展性更强。改动时对其他已经完善的业务影响极小,即更加符合开闭原则。针对该案例,超市的促销活动如果仅限于满减,打折两种的结合。那么就不用在CashContext类中增加复杂的逻辑,而且也不用再增加计算类。如果有更新的活动,比如积分等。可以新创建一个工厂和具体的产品类。以达到与其他业务的解耦性。