引入
我们通过一个题目来引入到今天的话题–工厂方法
某某单位准备开发一个职工工资打印程序,该工资需要打印职工工号、职工工资类别,工资数。(其中工资类别目前根据需求知道的有,Manager、Engineer还有其他的暂不知道如Salse等等。并且Manager类别的工资是年薪20万元,Engineer类别的工资是月薪5000元)该单位委托某IT企业进行开发,该开发团队的经理要求开发团队使用迭代递增开发方法,要求能适应工资类别变化这一需求,现在假设你是这位团队经理,要求给出这个工资打印程序的设计。
拿到这道题明确的告诉你了要使用迭代递增开发方法,要求能适应工资类别变化这一需求。而工厂模式就是解决可扩展性最佳的模式之一。
工厂方法:工厂方法是克服了简单工厂模式的缺点,主要是满足了OCP(Open-Closed Principle开闭原则)。
简单工厂模式的工厂类 随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。工厂方法模式完全满足OCP,使它有非常良好的扩展性。
工厂模式的结构
模式的结构中包括四种角色:
- 抽象产品(Product)
- 具体产品(ConcreteProduct)
- 工厂(Creator)
- 具体工厂(ConcreteCreator)
用UML工具Astah设计工厂模式
类图 :
顺序图:
主要代码:
用Java实现,主要利用了Java的类名反射机制。
Class.forName(empStr+“Factory”).getDeclaredConstructor().newInstance()
Client类
Client类为入口,主要工作为提供用户输入的接口
import java.lang.reflect.InvocationTargetException;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Scanner sc =new Scanner(System.in);
System.out.println("please input employee:");
String empStr = sc.next();
sc.close();
//
Employee employee;
IEmployeeFactory ief;
ief=(IEmployeeFactory)Class.forName(empStr+"Factory").getDeclaredConstructor().newInstance();
employee=ief.createEmployee();
employee.printSalary();
}
}
Employee类
Employee类为虚类,为主类调用工资打印程序提供接口,实现依赖倒转原则,为Engineer、Manager等的父类。
abstract public class Employee {
public abstract void printSalary();
}
Engineer类
打印工资程序的具体实例
public class Engineer extends Employee {
public void printSalary() {
System.out.println("Engineer salary is:10000");
}
}
工厂类:
IEmployeeFactory类
IEmployeeFactory类是工厂类的接口,这个是和简单工厂最大的区别。用接口和类名反射机制提前定义用于创建对象的接口,让子类(具体工厂)决定实例化具体的某一个类,即在工厂和产品中间增加接口(抽象工厂),工厂不再负责产品的创建,由接口针对不同条件返回具体的类实例,由具体类实例(具体工厂)去实现。
public class EngineerFactory implements IEmployeeFactory {
public Employee createEmployee() {
return new Engineer();
}
}
EngineerFactory类
public class EngineerFactory implements IEmployeeFactory {
public Employee createEmployee() {
return new Engineer();
}
}
其他的具体实现类就只用继承虚类Employee和工厂接口IEmployeeFactory类,创造一个新的实例。
工厂模式是对简单工厂模式进行了抽象。
有一个抽象的Factory类(可以是抽象类和接口),这个类将不在负责具体的产品生产,而是只制定一些规范,具体的生产工作由其子类去完成。
一个抽象工厂对应一个抽象产品,一个具体工厂对应一个具体产品,这个具体的工厂就负责生产对应的产品。
简单工厂的优缺点
优点
实现了算法和界面的分离,也就是将业务逻辑和界面逻辑分开了,降低了耦合度。
简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
缺点
- 没有遵守开放—封闭原则。
所谓的“开放-封闭”原则就是开放接口,封闭修改。如果将来需要添加一个开方的算法,那么,在简单工厂模式中,就必须在简单工厂类中添加相应的判断语句!另外,在简单工厂类中利用了Switch语句,这对程序的扩展本身就不不利。
工厂方法的优缺点
优点
工厂方法完全满足OCP,使其有非常良好的可扩展性和可维护性。
缺点
增加了类的数量,每有一个类需要打印就需要一个工厂类去匹配生产。
假如某个具体产品类需要进行修改,很可能需要修改对应的工厂类。
当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦。
下面同时也简单说明一下抽象工厂,抽象工厂就是在有多个抽象产品的时候对工厂模式进行的再一次的抽象扩展。它提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
工厂模式和抽象工厂的区别:
只有一个User类和User操作类的时候,只需要工厂方法模式; 但数据库中有很多的表,而Sql和access又是两大不同的分类,所以就延伸到了抽象工厂模式
抽象工厂模式的优点:
- 易于交换产品系列,由于具体工厂类,在一个应用程序中只需要在初始化的时候出现一次, 这就使得改变一个应用的具体工厂变得非常容易,它只需改变具体工厂即可使用不同的产品配置。
- 它让具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
抽象模式的缺点:
- 抽象模式虽然便于两数据库之间的切换,但是不便于增加需求功能。
- 如果有100个调用数据库访问的类,就需要多次实例化100此具体工厂类。
抽象工厂模式、反射以及配置文件:
反射方法的实质是在对象实例化的时候传引用,将程序由编译时转为运行时,通过字符串变量来处理,去除了、switch判断的麻烦。但是如果数据库在更换时,还需要去修改程序(字符串的值)重编译。通过添加配置文件可以解决更改DataAccess的问题。
工厂模式的适用范围
其实这一点在开篇的时候就提到了,(要使用迭代递增开发方法,要求能适应工资类别变化这一需求。而工厂模式就是解决可扩展性最佳的模式之一。)
当一个类不知道它所必须创建对象的类或一个类希望由子类来指定它所创建的对象时,当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候,可以使用工厂方法,支持多扩展少修改的OCP原则。