浅谈简单工厂模式、工厂方法模式、抽象工厂模式

浅谈简单工厂模式工厂方法模式抽象工厂模式

概述

简单说说对这几个设计模式的理解名字相近一几度弄不清这都是啥跟啥

都叫xx工厂模式,顾名思义,工厂是要生产产品(在编程中就是类的实例对象)的。而这三种模式都是生产型模式,区别在于生产实例的方式、时机以及所能够达到编程原则约束、灵活性不同。

二、前情须知

上一段如何的最后一句话“所能够达到编程原则约束、灵活性不同”怎么理解呢?这还得说说编程原则的事。这里我们着重关注一下“开放封闭”原则,该原则强调要能够对现有软件进行功能上的进行拓展,但不允许或者尽量少修改原有代码。

那么,假如你是个有心人这里应该就去思考——为什么要遵守这一原则呢举一个简单的例子吧ps:该例子参考程杰的《大话设计模式》里的故事)。假如小李负责维护你们公司的薪资管理系统当前全公司员工的薪资计算方式主要是两种:实习生的薪资计算方式,正式员工的薪资计算方式。而现在领导要求小李给这个系统拓展功能,新增一种薪资计算方式——临时工的薪资计算方式。那么请问,假如小李心术不正或者一时糊涂,拿到系统源码后,在其中加入一段代码,以一种不合法的手段抬高了自己的薪资,那么……。所以正确的做法是当初设计该薪资管理系统时让其符合开放封闭原则,当遇到需要新增薪资计算方式的时候,只要求小李写一个功能类放进去即可,而不至于非得把整个模块让他知道。这就是所谓的“能够对现有功能进行拓展,但不允许修改原有代码”。

说到编程原则插一句当初我懵懵懂懂问我的领导——编程的规范原则很多但是咱们在做东西的时候也没记得都遵守了啊领导是这样回答我的——在时间足够人力足够资金足够的条件下我可以做得要多优雅有多优雅但是现实情况往往是时间人力资金等一系列资源都不够的情况下去搞开发那就只能是有多少资源,做成什么样了。

从初学者Style说起

还以上述情景为例,如果一个初学者来编写这个薪资管理系统,那么八成会弄成这个样子:

package origin;
 
public class Salary {
 
public static void main(String[]args) {
// 员工身份
String identity = "实习生";
// 税前工资(实际情况值里应该是一个传入值)
int beforeTaxSalary = 17000;
// 经过“薪资管理系统”的计算得到实际薪资
double realSalary = calculateSalary(identity,beforeTaxSalary);
System.out.println(realSalary);
}
 
/**
 * 根据员工身份和税前工资计算应发工资
 *
 * @param identity
 *            员工身份(对应薪资计算方法)
 * @param beforeTaxSalary
 *            税前工资
 * @return 实际工资
 */
private static double calculateSalary(Stringidentity, int beforeTaxSalary) {
// 如果是实习生
if (identity.equals("实习生")) {
// 实习生的薪资算法
return 0.8 *beforeTaxSalary;
} else if (identity.equals("正式员工")) {
return beforeTaxSalary;
}
return beforeTaxSalary;
}
 
}
 


观看上述“初学者Style”的代码,我们会发现,如果要进行功能拓展(比如新增一个临时工薪资计算方式),那么我们小将上述代码文件中的calculateSalary方法打开然后在里面继续写else if语句这就印证了我在文章的第二部分前情须知里说说的,万一让一个心术不正的人去开发维护拓展功能,那将是多么危险的一件事呢。

简单工厂模式遵守了开放原则

下面我们来看看简单工厂模式是怎么解决上述危险问题的。

简单工厂抽象出了每种薪资计算方式的主要功能——根据员工身份和税前薪资计算应发工资所以将薪资计算方式都分割开来写在单独的文件中这些文件都实现抽象即可之后每次进行功能拓展只要添加实现了该抽象的类文件即可绘制成UML图就是下面这样:

 

Ps该模式只遵循了开放原则,并不遵循封闭原则。

以下是代码实现

package factory.simple;
 
/**
 * 薪资计算
 *
 * @author lxp
 *
 */
public abstract class AbstractSalaryCalculation {
 
/**
 * 税前薪资
 */
private int beforeTaxSalary;
 
/**
 * 薪资计算方法
 *
 * @return
 */
public abstract double calculateSalary();
 
public AbstractSalaryCalculation(int beforeTaxSalary) {
this.beforeTaxSalary =beforeTaxSalary;
}
 
public int getBeforeTaxSalary() {
return beforeTaxSalary;
}
 
public void setBeforeTaxSalary(int beforeTaxSalary) {
this.beforeTaxSalary =beforeTaxSalary;
}
 
}
 


package factory.simple;
 
/**
 * 正式员工的薪资计算类
 *
 * @author lxp
 *
 */
public class EmployeeCalculationextends AbstractSalaryCalculation {
 
public EmployeeCalculation(int beforeTaxSalary) {
super(beforeTaxSalary);
}
 
/**
 * 正式员工的薪资计算方式
 */
@Override
public double calculateSalary() {
return this.getBeforeTaxSalary();
}
 
}


 

package factory.simple;
 
/**
 * 实习生的薪资计算类
 *
 * @author lxp
 *
 */
public class PracticeCalculationextends AbstractSalaryCalculation {
 
public PracticeCalculation(int beforeTaxSalary) {
super(beforeTaxSalary);
}
 
/**
 * 实习生的薪资计算方式
 */
@Override
public double calculateSalary() {
return 0.8 *this.getBeforeTaxSalary();
}
 
}


 

package factory.simple;
 
/**
 * 简单工厂,用来生成薪资计算方式实例。
 *
 * @author lxp
 *
 */
public class SimpleFactory {
private Stringidentify;
 
public static AbstractSalaryCalculation getSalaryCalculation(Stringidentify, int beforeTaxSalary) {
// 此处不用switch,因为1.6以下不兼容。
AbstractSalaryCalculation salaryCalculation =null;
if (identify.equals("实习生")) {
salaryCalculation = new PracticeCalculation(beforeTaxSalary);
} else if (identify.equals("正式员工")) {
salaryCalculation = new EmployeeCalculation(beforeTaxSalary);
}
return salaryCalculation;
}
 
}


 

package factory.simple.test;
 
import factory.simple.AbstractSalaryCalculation;
import factory.simple.SimpleFactory;
 
public class TestSimpleFactory {
 
public static void main(String[] args) {
String practiceIdentify = "实习生";
String employeeIdentify = "正式员工";
int salary = 3000;
 
// 不同的身份,工厂创建的计算方式实例不同,结果也不一样。
AbstractSalaryCalculation salaryCalculation1 = SimpleFactory.getSalaryCalculation(practiceIdentify, salary);
System.out.println(salaryCalculation1.calculateSalary());
// 如果在有新的计算方式,只需添加实现了AbstractSalaryCalculation的类文件,然后修改主函数即可。
AbstractSalaryCalculation salaryCalculation2 = SimpleFactory.getSalaryCalculation(employeeIdentify, salary);
System.out.println(salaryCalculation2.calculateSalary());
}
 
}


 

 

五、工厂方法模式遵从了开放-封闭原则

在上一节中说道,简单工厂模式遵从了开放原则,但没有遵从封闭原则,不能算是真正意义上解决了我们之前提到的问题。那么接下来进化到工厂方法模式,它我们来看看工厂方法模式是如何彻底解决该问题的呢?呵呵,我个人觉得这种方法真是……

先来分析一下为啥每拓展一项新的功能就要改代码呢因为无论是“初学者style”还是后来的“简单工厂模式”,都需要在类文件里进行一些判断,就是if else的那些东西。每没新添加一种计算方式就需要打开这些文件添加新的ifelse分支所以造成了不遵从封闭原则那么工厂方法模式其实就是将简单工厂进行更高度的抽象拆分成一个个的生成薪资计算方式的工厂具体使用哪一个就让用户(也就是客户端)自己去选取好了。这样一来,就彻底遵从了开放-封闭原则

当然啦如此一来也引入了别的麻烦——每拓展一个新的功能就不仅仅需要添加该功能的类文件还需要添加生产该类的工厂的类文件UML图如下:


 

那么相应的代码如下

package factory.method.factory;
 
import factory.method.calculation.AbstractSalaryCalculation;
 
/**
 * 生产工厂的接口
 *
 * @author lxp
 *
 */
public interface ICreateSalaryCalculation {
AbstractSalaryCalculation createSalaryCalculation(int beforeTaxSalary);
}


 

package factory.method.factory;
 
import factory.method.calculation.AbstractSalaryCalculation;
import factory.method.calculation.PracticeCalculation;
 
public class CreatePracticeCalculationFactory implements ICreateSalaryCalculation {
 
@Override
public AbstractSalaryCalculation createSalaryCalculation(int beforeTaxSalary) {
AbstractSalaryCalculation calculation = new PracticeCalculation(beforeTaxSalary);
return calculation;
}
 
}


 

package factory.method.factory;
 
import factory.method.calculation.AbstractSalaryCalculation;
import factory.method.calculation.EmployeeCalculation;
 
public class CreateEmployeeCalculationFactory implements ICreateSalaryCalculation {
 
@Override
public AbstractSalaryCalculation createSalaryCalculation(int beforeTaxSalary) {
AbstractSalaryCalculation calculation = new EmployeeCalculation(beforeTaxSalary);
return calculation;
}
 
}


 

 

package factory.method.calculation;
 
/**
 * 薪资计算
 *
 * @author lxp
 *
 */
public abstract class AbstractSalaryCalculation {
 
/**
 * 税前薪资
 */
private int beforeTaxSalary;
 
/**
 * 薪资计算方法
 *
 * @return
 */
public abstract double calculateSalary();
 
public AbstractSalaryCalculation(int beforeTaxSalary) {
this.beforeTaxSalary =beforeTaxSalary;
}
 
public int getBeforeTaxSalary() {
return beforeTaxSalary;
}
 
public void setBeforeTaxSalary(int beforeTaxSalary) {
this.beforeTaxSalary =beforeTaxSalary;
}
 
}


 

 

 

package factory.method.calculation;
 
/**
 * 实习生的薪资计算类
 *
 * @author lxp
 *
 */
public class PracticeCalculationextends AbstractSalaryCalculation {
 
public PracticeCalculation(int beforeTaxSalary) {
super(beforeTaxSalary);
}
 
/**
 * 实习生的薪资计算方式
 */
@Override
public double calculateSalary() {
return 0.8 *this.getBeforeTaxSalary();
}
 
}


 

 

 

package factory.method.calculation;
 
/**
 * 正式员工的薪资计算类
 *
 * @author lxp
 *
 */
public class EmployeeCalculationextends AbstractSalaryCalculation {
 
public EmployeeCalculation(int beforeTaxSalary) {
super(beforeTaxSalary);
}
 
/**
 * 正式员工的薪资计算方式
 */
@Override
public double calculateSalary() {
return this.getBeforeTaxSalary();
}
 
}


 

package factory.method.calculation.test;
 
import factory.method.calculation.AbstractSalaryCalculation;
import factory.method.factory.CreateEmployeeCalculationFactory;
import factory.method.factory.ICreateSalaryCalculation;
 
public class TestMethodFactory {
 
public static void main(String[] args) {
// 实例化创建薪资计算方式的工厂,如果想拓展功能,新增一种薪资计算方式,只要添加实现了ICreateSalaryCalculation接口的文件和实现了AbstractSalaryCalculation的文件即可。客户端自己决定要采取的计算方法。
ICreateSalaryCalculation factory = new CreateEmployeeCalculationFactory();
// 工厂创建出薪资计算方式
AbstractSalaryCalculation salaryCalculation = factory.createSalaryCalculation(5000);
// 计算薪资
double realSalary = salaryCalculation.calculateSalary();
System.out.println(realSalary);
}
 
}


 

 

其实,工厂方法这种设计思想很普遍,像我们常用的软件中的插件,就是给开发插件的厂商留下一个接口,这个接口里定义了可以拓展的功能的抽象,然后各个厂商实现这个接口,就可以将自己的功能集成进去啦~\(≧▽≦)/~然后还有就是大家常用的框架里是不是经常出现XXXFactory之类的呢?嘻嘻,也是啦。

六、抽象工厂模式遵从了开放-封闭原则且不需要再客户端进行判断

话说上一小节中提到的模式不过是把判断挪到了客户端,让客户端进行判断到底该用那种方式计算薪资。

写到这里我才知道,原来这三个设计模式名字中都带有“工厂”,但是抽象工厂模式与前两个并没有什么太的的关系(我个人是这么认为的),不过抽象工厂模式通过“反射+配置文件”的确彻底遵从了开放-封闭原则,只不过你需要多一些开销啦——开发功能类、开发对应的工厂类、添加配置。

为了全文的连贯性,我们还是用上面的例子。不过这回换成了不是一家公司,而是多家公司的薪资计算了。老规矩,我们先上UML图:

 

代码如下:

package factory.abstracted.interfaze;
 
/**
 * 实习生的薪资计算接口
 *
 * @author 刘欣普
 *
 */
public interface IPracticeCalculation {
double calculatePracticeSalary(int beforeTaxSalary);
}


 

 

package factory.abstracted.interfaze;
 
/**
 * 正式员工的薪资计算接口
 *
 * @author 刘欣普
 *
 */
public interface IEmployeeCalculation {
double calculateEmployeeSalary(int beforeTaxSalary);
}


 

 

package factory.abstracted.calculation;
 
import factory.abstracted.interfaze.IPracticeCalculation;
 
public class CPracticeCalculationimplements IPracticeCalculation {
 
@Override
public double calculatePracticeSalary(int beforeTaxSalary) {
return beforeTaxSalary * 0.8;
}
 
}


 

 

 

package factory.abstracted.calculation;
 
import factory.abstracted.interfaze.IEmployeeCalculation;
 
public class CEmployeeCalculationimplements IEmployeeCalculation {
 
@Override
public double calculateEmployeeSalary(int beforeTaxSalary) {
return beforeTaxSalary;
}
 
}


 

 

 

package factory.abstracted.calculation;
 
import factory.abstracted.interfaze.IPracticeCalculation;
 
public class BPracticeCalculationimplements IPracticeCalculation {
 
@Override
public double calculatePracticeSalary(int beforeTaxSalary) {
return beforeTaxSalary * 0.8;
}
 
}


 

 

package factory.abstracted.calculation;
 
import factory.abstracted.interfaze.IEmployeeCalculation;
 
public class BEmployeeCalculationimplements IEmployeeCalculation {
 
@Override
public double calculateEmployeeSalary(int beforeTaxSalary) {
return beforeTaxSalary;
}
 
}


 

 

 

package factory.abstracted.calculation;
 
import factory.abstracted.interfaze.IPracticeCalculation;
 
public class APracticeCalculationimplements IPracticeCalculation {
 
/**
 * A公司的实习生薪资
 */
@Override
public double calculatePracticeSalary(int beforeTaxSalary) {
return beforeTaxSalary * 0.8;
}
 
}


 

 

 

package factory.abstracted.calculation;
 
import factory.abstracted.interfaze.IEmployeeCalculation;
 
public class AEmployeeCalculationimplements IEmployeeCalculation {
 
/**
 * A公司的正式员工薪资
 */
@Override
public double calculateEmployeeSalary(int beforeTaxSalary) {
return beforeTaxSalary;
}
 
}


 

package factory.abstracted.bean;
 
import factory.abstracted.interfaze.IEmployeeCalculation;
import factory.abstracted.interfaze.IPracticeCalculation;
 
public abstract class AbstractCompany {
 
/**
 * 创建计算实习生薪资的实例
 *
 * @return
 */
public abstract IPracticeCalculation createPracticeCalculation();
 
/**
 * 创建计算正式员工薪资的实例
 *
 * @return
 */
public abstract IEmployeeCalculation createEmployeeCalculation();
}


 

 

 

package factory.abstracted.bean;
 
import factory.abstracted.calculation.AEmployeeCalculation;
import factory.abstracted.calculation.APracticeCalculation;
import factory.abstracted.interfaze.IEmployeeCalculation;
import factory.abstracted.interfaze.IPracticeCalculation;
 
public class ACompanyextends AbstractCompany {
 
@Override
public IPracticeCalculation createPracticeCalculation() {
return new APracticeCalculation();
}
 
@Override
public IEmployeeCalculation createEmployeeCalculation() {
return new AEmployeeCalculation();
}
 
}


 

 

 

package factory.abstracted.bean;
 
import factory.abstracted.calculation.BEmployeeCalculation;
import factory.abstracted.calculation.BPracticeCalculation;
import factory.abstracted.interfaze.IEmployeeCalculation;
import factory.abstracted.interfaze.IPracticeCalculation;
 
public class BCompanyextends AbstractCompany {
 
@Override
public IPracticeCalculation createPracticeCalculation() {
return new BPracticeCalculation();
}
 
@Override
public IEmployeeCalculation createEmployeeCalculation() {
return new BEmployeeCalculation();
}
 
}


package factory.abstracted.bean;
 
import factory.abstracted.calculation.CEmployeeCalculation;
import factory.abstracted.calculation.CPracticeCalculation;
import factory.abstracted.interfaze.IEmployeeCalculation;
import factory.abstracted.interfaze.IPracticeCalculation;
 
public class CCompanyextends AbstractCompany {
 
@Override
public IPracticeCalculation createPracticeCalculation() {
return new CPracticeCalculation();
}
 
@Override
public IEmployeeCalculation createEmployeeCalculation() {
return new CEmployeeCalculation();
}
 
}


package factory.abstracted.test;
 
import factory.abstracted.bean.ACompany;
import factory.abstracted.bean.AbstractCompany;
 
public class TestAbstractedFactory {
 
public static void main(String[]args) {
/**
 *注意下面这一行代码,当需要切换“产品系列”的时候,直接换掉这里就可以了。
 * 注意,利用配置文件可以不需要修改代码,写成类似于
 * AbstractCompany Company = Class.forName("XXX");此处的“XXX”取自配置文件
 */
AbstractCompany Company = new ACompany();
System.out.println("该公司的正式员工工资是" +Company.createEmployeeCalculation().calculateEmployeeSalary(3000));
System.out.println("该公司的实习生 工资是" +Company.createPracticeCalculation().calculatePracticeSalary(3000));
}
 
}


 

抽象工厂模式的有点是易于切换产品系类,但是缺点是当我需要添加新的“产品(薪资计算方式,例如UML图中画的临时工薪资计算方式)”的时候,需要添加的不仅仅是“产品”,还有生产该种产品的“工厂”。如果在客户端想调用,需要在配置文件中添加该工厂类信息以便反射获取实例。

其实该设计模式很常见,我们常用的hibernate框架就运用了这种设计模式。Hibernate运用配置决定使用哪种数据库,又运用配置将类的DAO实例关联。



ps:如果图片看不清楚,请查看原版。

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值