Skip to content
Sev7e0

工厂方法-FactoryMethod

设计模式1 min read

定义

Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)

角色

Product:抽象产品类

ConcreteProduct:具体产品类

Creator:抽象工厂类

ConcreteCreator:具体工厂类

  • 抽象产品类Product 负责定义产品的共性。
  • Creator为抽象工厂,具体何时创建产品类是由具体工厂类完成。

类图

MUBt5hHoZCcwXV7

使用场景

工厂方法模式可以说是替代了我们传统的再代码中 new 一个对象的方式,所以再任何需要创建对象的地方都可以使用工厂方法模式,不过要考虑这样做是否值得,会增加代码的复杂度。

在遇到需要灵活且方便拓展的项目中,可以考虑使用工厂方法,当有新的功能出现,那么可以考虑增加一个 product 类就可以了。例如系统中一个拦截模块,可能最开始要实现的是根据 ip 拦截,在后期又要根据方法进行拦截,又或者根据接口进行拦截,这时使用工厂方法今可以做到灵活拓展。

优点

  • 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
  • 工厂方法模式的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。
  • 屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。
  • 工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类。

缺点

注意事项

具体实现

抽象产品类

1public abstract class Product{
2 //公共方法
3 public void common(){
4 System.out.println("this is common method!")
5 }
6 //抽象方法 交给子类实现
7 public abstract void make();
8}

具体产品类

1public class ConcreteProduct extends Product{
2 public void make(){
3 System.out.println("this is ConcreteProduct")
4 }
5}

抽象工厂类

1public abstract class Factory{
2 public abstract <T extends Product> create(Class<T> class);
3}

具体工厂类

1public class ConcreteFactory extends Factory{
2 public <T extends Product> create(Class<T> class){
3 Product product=null;
4 try {
5 product =(Product)Class.forName(c.getName()).newInstance();
6 } catch (Exception e) {
7 //异常处理
8 }
9 return (T)product;
10 }
11}

使用场景

1public class Client {
2 public static void main(String[] args) {
3 Creator creator = new ConcreteCreator();
4 Product product = creator.createProduct(ConcreteProduct1.class);
5 product.make();
6 product.common();
7 }
8}

拓展

工厂方法有对拓展极为开放,也就是说他可以轻松实现多中拓展,而且可以与其他模式结合使用。

缩小为简单工厂模式

若在项目中仅需要一个工厂类,并且能够进行对象的创建,那么其实只需要一个简单工厂模式就可以了,将上边的例子改造一下。

  • 修改抽象工厂类,去掉具体工厂类
1public class Factory{
2 public static <T extends Product> create(Class<T> class){
3 Product product=null;
4 try {
5 product =(Product)Class.forName(c.getName()).newInstance();
6 } catch (Exception e) {
7 //异常处理
8 }
9 return (T)product;
10 }
11}
  • 修改使用场景类
1public class Client {
2 public static void main(String[] args) {
3 //删除
4 //Creator creator = new ConcreteCreator();
5 //对 static 直接调用
6 Product product = Creator.createProduct(ConcreteProduct1.class);
7 product.make();
8 product.common();
9 }
10}

运行结果没有发生变化,但是我们的类图变简单了,而且调用者也比较简单,该模式是工厂方法模式的弱化,因为简单,所以称为简单工厂模式(Simple Factory Pattern),也叫做静态工厂模式。在实际项目中,采用该方法的案例还是比较多的,其缺点是工厂类的扩展比较困难,不符合开闭原则,但它仍然是一个非常实用的设计模式。

拓展为多个工厂类

当我们在做一个比较复杂的项目时,经常会遇到初始化一个对象很耗费精力的情况,所有的产品类都放到一个工厂方法中进行初始化会使代码结构不清晰。例如,一个产品类有5个具体实现,每个实现类的初始化(不仅仅是new,初始化包括new一个对象,并对对象设置一定的初始值)方法都不相同,此时就可以考虑使用多个具体工厂类实现,也就是说每个工厂做自己特定的业务

  • 新增一个具体工厂类
1public class NewConcreteFactory extends Factory{
2 public Product create(){
3 return new NewProduct();
4 }
5}

其实create方法中已经不再需要参数了,因为针对每一种产品都有自己的独立的工厂。

每一个产品都对赢了一个创建类,使得他们之间每个工厂的任务简单,代码结构清晰,单对于拓展性和维护性这是带来了一定的负面影响,若一味地在增加产品的时候就增加工厂,可能会导致类的成倍增长。

在复杂的应用中一般采用多工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。

总结

工厂方法模式在项目中使用得非常频繁,以至于很多代码中都包含工厂方法模式。该模式几乎尽人皆知,但不是每个人都能用得好。熟能生巧,熟练掌握该模式,多思考工厂方法如何应用,而且工厂方法模式还可以与其他模式混合使用(例如模板方法模式、单例模式、原型模式等),变化出无穷的优秀设计,这也正是软件设计和开发的乐趣所在。

参考

设计模式 设计模式之禅