实际开发中哪些场景需要用到工厂模式?

昌维
  • 4.5k

工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。

  1. 工厂模式

  2. 简单工厂模式

  3. 抽象工厂模式

请问实际开发中哪些情况下会用到它?为什么我感觉我现在开发很少会用到这些设计模式啊。。。

回复
阅读 13.3k
7 个回答

我先说下 我目前看到用到了工厂模式的例子:

一般的MVC框架中,都有一个基本的DB数据库基本操作类
我叫它DB class,有一个baseModel class 去继承 db class
baseModel 是所有框架model的基类,需要继承baseModel
baseModel已经有db类的 增删查改的方法了,baseModel其实就是数据库工厂,不同的模型继承baseModel,就有操作不同数据表的对象实例了,这样就用一个基础的class 完成了实例化各个不同数据表的对象,就好像是工厂一样,传不同的表名字就返回给你不同的对象。
我的理解就是这样的,如有误,还请包涵和斧正。

不论是工厂模式还是其它创建型模式,都是一个目的——为了初始化一个对象。或者说,为了构建一个数据结构模型(类和对象本身就是一种自定义的数据结构)。

那么,问题来了,为什么有 new 这样方式可以创建一个对象,还要使用设计模式。本质上就是一个原因,不想让上层使用者直接使用 new 来初始化对象。

这样的原因有很多,绝大多数原因就是对上层的使用者隔离对象创建的过程;或者是对象创建的过程复杂,使用者不容易掌握;或者是对象创建要满足某种条件,这些条件是业务的需求也好,是系统约束也好,没有必要让上层使用者掌握,增加别人开发的难度。

所以,到这时我们应该清楚了,无论是工厂模式,还是上面的战友说的开闭原则,都是为了隔离一些复杂的过程,使得这些复杂的过程不向外暴露,如果暴露了这些过程,会对使用者增加麻烦,这也就是所谓的团队合作。

面向对象封装的本身也就是为了使得对外的 API 尽可能的简化。

例如,你定义了一个 Status字段,但这个字段因为某些业务原因,需要使用整数来表示状态。那么,如果数字少了还好办,如果数字多了,上层使用者就不一定能记清楚每个数字代表的状态(比如你要做语音通信系统,那么,语音设备是有很多状态数字的)。这时,如果使用 new来创建对象,然后再对 Status 进行赋值,不可避免的,可能要查阅开发文档,或者会不小心给出一个错误的值。这时,你就不妨使用工厂模式,或者其它合适的设计模式,来进行代码的建设。

比如,这样:

public static class Factory
{
    public static Ixxxxxx CreateWithOpen()
    {
        var obj = new Obj();
        obj.Status = 1;
        return obj;
    }
    public static Ixxxxxx CreateWithClose()
    {
        var obj = new Obj();
        obj.Status = 2;
        return obj;
    }
}

当然,使用枚举也行,这个说白了,就是看设计者的意愿了。

所以,设计模式没有说必需在哪个场景中使用,更确切的说,应该是,当你使用了设计模式,能不能为你的团队成员带来方便,或者提升代码质量,避免一些错误。如果是,就用,如果仅仅带来了复杂,并没有益处,那还是算了。

一句话,没有该不该用,也没有哪些需要不需要用,用就要带来效益,无论是对团队还是产品质量或产品的可维护性。用不用,要以团队配合和产品为导向,这才是对一个软件设计师的基本要求。

想理解工厂模式的话就不能不知道简单工厂模式了。

 switch ($type) { 
      case '存款职员': $man = new Depositer; 
      break;
      case '销售': $man = new Marketer; 
      break; 
      case '接待': $man = new Receiver; 
      break; 
      default: echo '传输参数有误,不属于任何一个职位'; 
      break; 
  }

诺,这就是简单工厂模式,是不是很常见,简单工厂模式有一个不足,他虽然遵循了单一职责原则,但它违反了另一条很重要的原则:开放封闭原则。如果新增一个文员职位,那么我们还要修改对应代码,增加一个case,这是很可怕的,因为写好的代码如果我们再去修改可能会造成未知的效果。

而工厂模式就是对简单工厂的一次升级,这里以MVC里的DB class来说明,外部调用的时候只需选择自己所需的表名,该工厂会去调用真实数据库处理方法,然后返回你想要的结果。

工厂模式是一个用于实例化对象的模式,是用工厂方法代替new操作的一种方式。工厂模式在Java项目中到处都是,因为工厂模式就相当于创建实例对象的new,如在我们的系统中经常需要记日志,如果创建logger实例时所做的初始化工作可能是很长一段代码,可能要初始化、赋值、查询数据等等,则会导致代码臃肿而难看。

    private static Logger    logger = LoggerFactory.getLogger(MyBusinessRPC.class);
    
 public static Logger getLogger(String name) {
    ILoggerFactory iLoggerFactory = getILoggerFactory();
    return iLoggerFactory.getLogger(name);
  }

public static ILoggerFactory getILoggerFactory() {
    if (INITIALIZATION_STATE == UNINITIALIZED) {
      INITIALIZATION_STATE = ONGOING_INITIALIZATION;
      performInitialization();
    }
    switch (INITIALIZATION_STATE) {
      case SUCCESSFUL_INITIALIZATION:
        return StaticLoggerBinder.getSingleton().getLoggerFactory();
      case NOP_FALLBACK_INITIALIZATION:
        return NOP_FALLBACK_FACTORY;
      case FAILED_INITIALIZATION:
        throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
      case ONGOING_INITIALIZATION:
        // support re-entrant behavior.
        // See also http://bugzilla.slf4j.org/show_bug.cgi?id=106
        return TEMP_FACTORY;
    }
    throw new IllegalStateException("Unreachable code");
  }

在java web项目开发过程中,经常会看到如下配置文件:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="mapperLocations" value="classpath*:com/**/*Mapper*.xml" /> 
    <property name="dataSource" ref="dataSource" />
    <property name="typeAliasesPackage" value="com.alibaba.***.dal" />
</bean>

bean的name属性sqlSessionFactory,我们知道Sqlsession对应着一次数据库会话。由于数据库回话不是永久的,因此Sqlsession的生命周期也不应该是永久的,相反,在你每次访问数据库时都需要创建它(当然并不是说在Sqlsession里只能执行一次sql,你可以执行多次,当一旦关闭了Sqlsession就需要重新创建它)。创建Sqlsession的地方只有一个,那就是SqlsessionFactory的openSession方法,这里也用到了工厂模式。

设计模式的根本目的是减少项目变化所造成的影响,这一点要牢牢记住!
工厂模式是把项目当中的变化点抽取封装出来.至于哪些是变化点,哪些该抽象需要开发者自行观察和预测.

举个不恰当的例子
比如我的网站有多个页面每个页面都有css文件

//样式路径获取工厂类
public class MyStyleFactory
{
  public string Page1Css(){return cssPath;}
  public string Page2Css(){return cssPath;}
  //.......
}

假设项目中html的link链接就是通过代码生成的.
那么显然每个页面都会要求获得css的路径.
且css路径也是非常有可能变化的,那么这就是变化点,于是我可以把获取路径的这些变化点封装到MyStyleFactory中.这样以后修改只要改MyStyleFactory,而不是切换到各个页面修改.

这就是简单工厂.

但是一个网站可能有多种主题的样式,而MyStyleFactory仅能获取一个主题的样式,如果我想切换样式呢?
于是切换样式就成了变化点,因封装他.换句话说我的系统是会切换Factory的.

为了适应Factory的切换,使用Factory的地方应该依赖于抽象,于是要有个抽象的工厂.

abstract class AbstractFactory
{
   public abstract string Page1Css();
   public abstract string Page2Css();
   //.......
}

写多个实现AbstractFactory的样式工厂

在使用的时候

Client(){
  AbstractFactory factory=new MyStyleFactory();
}

你会发现只有一个地方要变化
这就是抽象工厂

上面说到这个例子是不恰当的.如果页面数量确定下来还好说,但实际项目中变化的不仅仅是页面的样式(变化页面主题),页面的数量也是变化的,于是AbstractFactory中的Page数字Css方法的数量是不确定的,每添加一个页面就要加一个方法,并且每个继承类都要修改,这种变化用抽象工厂是不适合的,所以这时候使用抽象工厂反而是错的.

所以一定要弄清楚项目需求以及设计模式的应用场景

sunfeng
  • 5
新手上路,请多包涵

工厂的职能就是你给它一个模型或者具体的样品需求,它给你一个成品。工厂模式也是这样的道理,比如,你入参是a,它就给你一个A对象,你入参b,它就给你生产一个B对象,这里a,b就是你让工厂生产的商品具体需求,如长宽高等。

工厂你可以理解为隐藏了内部细节,你调用工厂的生产API ,直接获得所描述的物体,具体怎么生产的,你不用去关注细节,因为有的东西简单,直接new出来就可以了,但有的很复杂,比如spring的注入链。要理解工厂模式,建议看看spring实现的factory。

你知道吗?

宣传栏