概念
定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行.工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)
应用场景
由于简单工厂只提供一个工厂类来进行对象的创建,导致其职责过重,且代码不易维护,当系统需要扩展时,需要修改原有代码,导致违反了开闭原则,故引出工厂方法模式。如下情况可以考虑使用工厂方法模式
- 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体类名,只需要知道对应的工厂即可,具体的产品由具体工厂对象进行创建,可以将具体工厂类的类名存储在配置文件或者数据中,进行动态创建
具体应用
工厂方法模式就是利用了面向对象的多态性,提供抽象工厂类型,让其实现类(具体工厂类)来决定要创建的具体对象
该模式主要包含如下角色:
- Factory(抽象工厂):在抽象工厂类中,声明了一个创建方法,用于返回一个抽象产品
- ConcreteProduct(具体工厂):是抽象工厂的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品实例
- Product(抽象产品):定义产品的接口,是工厂方法所创建对象的公共父类
- ConcreteProduct(具体产品):实现了抽象产品接口,某种类型由专门的具体工厂进行创建,具体工厂和具体产品之间一一对应
代码实践
还是绘制图形,我们使用工厂方法模式来进行绘制图形,并且增加了一个使用案例,即通过读取配置文件+反射的机制来实现运行时动态生成不同的对象。 首先定义一个产品抽象类,这里我们的产品就是图形Graphical
/**
* 抽象产品类
* circular 圆形
* triangle 三角形
*/
public abstract class Graphical {
public abstract void draw();
public abstract void erase();
}
定义我们的具体产品,这里具体的产品为三角形,和圆形。不支持方形
public class Circular extends Graphical {
@Override
public void draw() {
System.out.println("画了一个圆形");
}
@Override
public void erase() {
System.out.println("擦掉了一个圆形");
}
}
public class Triangle extends Graphical {
@Override
public void draw() {
System.out.println("画了一个三角形");
}
@Override
public void erase() {
System.out.println("擦掉了一个三角形");
}
}
然后定义抽象工厂类GraphicalFactory
public interface GraphicalFactory {
public Graphical createGraphical();
}
定义具体工厂类,因为我们有2个具体产品(三角形和圆形),所以有2个具体工厂(CircularGraphicalFactory和TriangleGraphicalFactory)
//圆形工厂
public class CircularGraphicalFactory implements GraphicalFactory {
@Override
public Graphical createGraphical() {
return new Circular();
}
}
//三角形工厂
public class TriangleGraphicalFactory implements GraphicalFactory {
@Override
public Graphical createGraphical() {
return new Triangle();
}
}
定义通过配置文件创建对象的工具类ConfigUtil
public class ConfigUtil {
public static Object getBean() {
try {
InputStream in = Test.class.getClassLoader().getResourceAsStream("Graphical.properties");
Properties props = new Properties();
InputStreamReader inputStreamReader = new InputStreamReader(in, "UTF-8");
props.load(inputStreamReader);
String cName=props.getProperty("factoryClassName");
//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
配置文件 Graphical.properties
factoryClassName=com.donkeyx.pattern.factory.factorymethod.TriangleGraphicalFactory
测试类
public class Test {
public static void main(String[] args) {
System.out.println("---------------------------------使用默认方式进行创建----------------------------------");
defaultMethod();
System.out.println("---------------------------------读取配置文件方式进行动态创建----------------------------------");
readConfigFile();
}
/**
* 使用默认方式进行创建
*/
public static void defaultMethod(){
//创建一个圆形
GraphicalFactory factory;
factory = new CircularGraphicalFactory();
Graphical circular = factory.createGraphical();
circular.draw();
circular.erase();
//创建一个三角形
factory = new TriangleGraphicalFactory();
Graphical triangle = factory.createGraphical();
triangle.draw();
triangle.erase();
}
/**
* 读取配置文件方式进行动态创建
*/
public static void readConfigFile() {
GraphicalFactory factory;
factory = (GraphicalFactory)ConfigUtil.getBean();
Graphical graphical = factory.createGraphical();
graphical.draw();
graphical.erase();
}
}
结果
优点
- 在新产品加入时,无须修改抽象工厂和抽象产品的接口,无须修改客户端,也无需修改其他的具体工厂和具体产品,只需要再加入一个具体工厂和具体产品就可以了,符合开闭原则,提高了系统的可扩展性
缺点
- 在新产品加入时,需要再加入一个具体工厂和具体产品,类的个数容易过多,增加了代码结构的复杂度
- 增加了系统的抽象性和理解难度