文章目录
1. 前言
在简单工厂模式读书笔记中,介绍了用简单工厂模式实现一个简易计算器功能,在本章,我们将介绍一种新的设计模式——工厂方法模式,去实现计算器功能
2. 概念
工厂方法模式定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
3. UML类图
4. 工厂方法模式实现
现在需要在原来的计算器增加一种新的工厂和两种新的运算类(以后可以扩展更多的高级运算,比如正余弦、正余切等),不要影响原有的代码和运作。
4.1 UML类图
4.2 两个运算类
public class Pow extends Operation{
public double getResult(double numberA,double numberB){
return Math.pow(numberA, numberB)
}
}
public class Log extends Operation{
public double getResult(double numberA,double numberB){
return Math.log(numberB)/Math.log(numberA);
}
}
4.3 工厂接口
public interface IFactory{
public Operation createOperation();
}
4.4 基础运算工厂类
//基础运算工厂
public class FactoryBasic implements IFactory{
public Operation createOperation(String operType){
Operation oper = null;
switch (operType){
case "+":
oper= new Add();
break;
case "-":
oper= new Sub();
break;
case "*":
oper= new Mul();
break;
case "/":
oper= new Div();
break;
}
return oper;
}
}
4.5 高级运算工厂类
//高级运算工厂
public class FactoryAdvanced implements IFactory{
public Operation createOperation(String operType){
Operation oper = null;
switch (operType){
case "pow":
oper= new Pow();
break;
case "log":
oper= new Log();
break;
}
return oper;
}
}
4.6 运算工厂类
public class OperationFactory{
public static Operation createOperate(String opera){
Operation oper = null;
IFactory factory = null;
switch (operate){
case "+":
case "+":
case "+":
case "+":
//基础运算工厂实例
factory = new FactoryBasic();
break;
case "pow":
case "log":
factory = new FactoryAdvanced();
break;
}
//利用多态返回实际的运算类实例
oper = factory.createOperation(operate);
return oper;
}
}
OperationFactory类已经不存在运算子类实例化的代码了。也就是说,在这个代码里,全部是接口与具体工厂类,并不存在具体的实现,与原来的OperationFactory类对比,实例化的过程延迟到了工厂子类中。
5. 商场收银软件升级
在策略模式读书笔记中,介绍了用装饰模式去设计商场收银软件,观察下面红框的代码:
在这个CashContext类中,case5和6显得特别的复杂,因此,我们尝试用工厂方法模式去合并一部分代码。
思路如下:
如果有“先打折后满减类”存在,那它应该有三个初始化参数:折扣值、满减条件、满减返利值,那么打折类,其实就是满减返利值条件为0的情况,另外满减类,就相当于折扣参数为1的情况,因此,我们可以抽象成两个工厂:先打折再满减和先满减再打折两个工厂类。
下面给出UML类图和对应的实现代码。
5.1 UML类图
5.2 IFactory接口
public interface IFactory{
public Operation createSalesModel();//创建销售模式
}
5.3 IFactory接口的两个类
IFactory接口的两个类分别是先打折再满减类和先满减再打折类
//先打折再满减
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;
}
}
//先满减再打折
public class CashReturnRebateFactoryimplements 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(cr1); //满m返n装饰满m返n的算法
return cr2;
}
}
"有了上面的这些准备后,CashContext类就简单多了,它针对的是ISale 接口、IFactory接口编程,然后两个工厂类,对于各个打折满减算法 CashSuper、CashNormal、CashReturn、CashRebate等具体类一无所知。实现了松耦合的目的。
5.4 CashContext类
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(1d,300d,100d);
break;
case 5://先打8折,再满300返100
fs = new CashRebateReturnFactory(0.8d,300d,50d);
break;
case 6://先满300返100,再打8折
fs = new CashReturnRebateFactory(1d,0d,0d);
break;
}
this.cs = fs.createSalesModel();
}
public double getResult(double price,int num){
//根据收费策略的不同,获取计算结果
return this.cs.acceptCash(price,num);
}
}
6. 总结
- 工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。
- 对于复杂的参数的构造对象,可以很好地对外层屏蔽代码的复杂性。
- 很好的解耦能力。