一、定义
Define an interface for creating an object,but let subclasses decide which class to
instantiate.Factory Method lets a class defer instantiation to subclasses.即定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类来完成
主要的目的是定义一个创建对象的接⼝,让其⼦类⾃己决定实例化哪一个具体的工厂类,⼯⼚模式使其创建具体实例的过程延迟到子类进行。
简单总结就是为了提供代码结构的可扩展性,屏蔽每⼀个功能类中的具体实现逻辑。让外部可以更简单的调用即可
二、类图
三、四个重要角色
1.Factory:工厂接口角色
工厂接口角色是所创建的具体产品工厂类的父类或接口,负责描述所有实例对象的共同接口或抽象类。规定了工厂对象的共同方法,是公具体工厂的公共接口。
2.具体工厂类角色
工厂接口角色的子类,用来被外界调用,描述具体的工厂;实现FactoryMethod工厂方法创建具体产品的实例
3.Product:抽象产品角色
抽象产品角色是所创建的具体产品类的父类或接口,负责描述所有实例对象的共同接口或抽象类。规定了产品对象的共同方法。
4.ConcreteProduct:具体产品角色
实现了抽象产品类或接口,定义了具体产品的特定行为和属性,所有创建的对象都充当这个角色的某个具体类的实例。
四、实际案例
1.定义抽象产品角色接口
public interface LightingProduct {
//定义一个生产节能灯产品的方法
public void energySavingLightProduct();
}
2.定义两个具体品牌的节能灯产品
public class PanasonicLighting implements LightingProduct{
@Override
public void energySavingLightProduct() {
System.out.println("松下-节能灯");
}
}
public class PhilipsLighting implements LightingProduct{
@Override
public void energySavingLightProduct() {
System.out.println("飞利浦-节能灯");
}
}
3.定义工厂方法接口
public interface FactoryMethod {
public LightingProduct makeLightingProduct();
}
4.定义两个具体工厂生产具体节能灯产品
//生产松下节能灯的工厂
public class PanasonicLightingFactory implements FactoryMethod{
@Override
public LightingProduct makeLightingProduct() {
return new PanasonicLighting();
}
}
//生产飞利浦节能灯的工厂
public class PhilipsLightingFactory implements FactoryMethod{
@Override
public LightingProduct makeLightingProduct() {
return new PhilipsLighting();
}
}
5.模拟产品的订购
public class FactoryMethodMain {
public static void main(String[] args) {
//订购松下节能灯
FactoryMethod factoryMethod = new PanasonicLightingFactory();
factoryMethod.makeLightingProduct().energySavingLightProduct();
//订购飞利浦节能灯
factoryMethod = new PhilipsLightingFactory();
factoryMethod.makeLightingProduct().energySavingLightProduct();
}
}
6.订购结果
松下-节能灯
飞利浦-节能灯
7.通用工厂类改造
上述第4步中,如果需要创建多中类型的节能灯时,那边需要创建大量的对应的工厂类,那么是否可以进行对具体工厂类进行改造呢?答案是可以的,使用泛型+反射的方式来实现
①.通过工厂接口定义
public interface GeneralFactoryMethod {
//泛型对象继承LightingProduct
public <T extends LightingProduct> T makeLightingProduct(Class<T> clazz);
}
②.通用工厂方法实现
public class GeneralLightingFactory implements GeneralFactoryMethod{
@Override
public <T extends LightingProduct> T makeLightingProduct(Class<T> clazz) {
LightingProduct lightingProduct =null;
try {
//通过反射来创建需求定制的产品
lightingProduct = (LightingProduct)Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
return (T)lightingProduct;
}
}
③.订购商品
public class FactoryMethodMain {
public static void main(String[] args) {
//创建通用工厂对象
GeneralFactoryMethod generalFactoryMethod = new GeneralLightingFactory();
//订购松下节能灯
generalFactoryMethod.makeLightingProduct(PanasonicLighting.class).energySavingLightProduct();
//订购飞利浦节能灯
generalFactoryMethod.makeLightingProduct(PhilipsLighting.class).energySavingLightProduct();
}
}
④.订购结果
松下-节能灯
飞利浦-节能灯
五、适用场景
①.一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类
②.一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展
③.将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
六、工厂方法模式的优缺点
1.优点
①.在工厂方法模式中,工厂方法用来创建客户具体所需要的产品,用户只需要关心所需产品对应的工厂,无须关心其具体的创建细节,甚至无须知道具体产品类的类名。
②.基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。因此工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
③.使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了
2.缺点
①.在添加新的产品时,需要重新编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
②.由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
参考资料:工厂方法模式(Factory Method Pattern) — Graphic Design Patterns