[转载]什么是工厂模式

前日在网上看到一篇关于工厂模式介绍的文章.作者写的很好.所以自己转载一份.更利于跟我一样的菜鸟阅读.不喜勿喷..如原作者看到有任何涉及个人版权问题.请与我联系.我及时删除文章.同时感谢原作者的教程.很浅显易懂.

 

 

正文部分:

 

 

1、  传授设计模式中存在的问题

  我个人最近对设计模式中的工厂模式进行了比较深入的学习,通过学习感悟出,现在很多设计模式传道者,在讲述设计模式的过程中存在一些问题,使得设计模式变得很难理解。设计模式本身很抽象,但是这些传道者在讲述的过程中,将一个抽象的东西讲的更抽象,从而使我们望而却步。有些人在讲述的时候,没有考虑读者的注意力。比如我在看《C#设计模式》的时候,在讲述抽象工厂模式的时候,直接就进入了一个示例,而且这个例子比较复杂,涉及了比较多的概念、术语,而且还有算法要处理。但是这些和要讲述的核心内容无关,我想要看懂他的示例就要将这个东西都搞懂,就分散了我的注意力。我个人总结,学习设计模式的方法是,首先找到一个突破口,比如可以先学习构造型模式中简单的模式,将它理解、熟练应用。通过对一、两个模式有一个深入的认识之后,再学习其它比较复杂一点的模式就容易多了,这是一种迭代的思想在学习中的应用。另外学习任何一种设计模式的过程应该是具体-抽象-再具体这个的一个过程。这句话的意思是首先通过一个比较具体一点的例子来帮助理解设计模式,在理解之后将你的理解扩展到解决这一类问题,上升到一定的理论高度。然后就是再到具体,也就是应用设计模式,应用理论解决自己遇到的实际问题。

 
2、学习工厂模式的预备知识:

  首先声明这些预备知识并不是工厂模式仅仅需要,因为我先讲述工厂模式,所以在学习工厂模式之前将这些问题提出。

2.1 Upcasting:

  Upcasting中文翻译有好几个,比如向上类型转换、向上转型、上溯造型。我个人比较喜欢向上转型这个翻译,即简单又含义明确。向上转型这个概念,我在Bruce Eckel的Thinking in c++、Thinking in Java中都看到过,我不是很确定这个概念是否是他提出来的。向上转型是把一个派生类当作它的基类使用。我们将一个更特殊的类型转换到一个更常规的类型,这当然是安全的。派生类是基类的一个超集。它可以包含比基类更多的方法,但它至少包含了基类的方法。向上转型给我们带来的好处就是我们可以将不同的派生通过一种统一的方式进行处理。向上转型带来的弊端就是我们向上转型的过程会丢失派生类的接口。既然有向上转型,也就有向下转型即DownCasting,我们在此不做详细讨论。下面使用一个例子来示例向上转型。

2.2 多态

  我不敢想象离开了多态后的设计模式是一个什么样子。什么是多态,我喜欢总结这样一句话来回答这个问题,“一个接口,多种实现”。注意这里的接口不仅仅表示Interface关键字,是广义上的接口。在C#中实现接口我们有两种途径,一种是借助继承来实现,一种是借助Interface来实现。

3、工厂设计模式理论

3.1 概述

  工厂模式具体包括了简单工厂、工厂方法、抽象工厂,它们是按照从简单到复杂的顺序排列的,属于设计模式中的创建型,其中简单工厂并不属于GOF的23中模式。但是它是理解其它的工厂模式的一个很好的基础,所以很多人在讲述设计模式的时候会提到简单工厂模式。创建型模式关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑。

3.2 不使用任何模式

  我们现在有这样的一个设计,影像家电(VideoWiring)包括了DVD、VCD。在基类VideoWiring中有PlayVideo方法,子类重载了这个方法。

 

  我们如何来调用PlayVideo进行播放呢。我们可以看到下面的代码可以实现。


  下面是调用对象的方法进行播放的代码:

上面的代码可以实现功能但是不好,为什么呢?类实现了多态,但是我们在调用的时候并没有利用多态。如果我们有很多的影像家电产品,就需要写很多的类似

  下面是使用多态完成播放功能的代码:

无论是什么影像家电产品,我们都可以使用一个统一的方式进行播放,即vw.PlayVideo()。
  我们再讨论一下,上面的代码存在的问题。虽然上的代码很短,应该不会有问题,但是我们定位的目标应该更高些,应该考虑怎样达到良好的封装效果,减少错误修改的机会。我们自然的应该考虑对象创建的问题了,能不能让不同的影像家电产品的创建方式相同,而且这个创建过程对使用者封装,也就是说让对象的创建象播放功能那样简单、统一。如果能够实现,会给我们的系统带来更大的可扩展性和尽量少的修改量。“哇!那该多好呀”。“不要羡慕了,来看看简单工厂模式,听说它能够实现”。

3.3 简单工厂模式

  我们使用简单工厂对上面的代码继续改进,根据上面的分析我们考虑对对象创建进行近一步的封装。使用一个类专门来完成对对象创建的封装,这个类我们称为工厂,因为它的作用很单一就生成出一个个的类。下面是一个工厂类的示例代码:

这样我们的客户端代码又可以更加有效简洁了:

  注意:在上面的两段代码示例中我们就已经使用了向上转型。首先注意在Create类的factory方法中使用了return new DVD();这样的语句,但是这个函数的返回值却是VideoWiring,它DVD类的基类。所以我们的客户程序才可以使用VideoWiring vw=Create.factory("DVD")这样的语句。这样客户程序并不关心创建是如何完成的,以及创建的对象是什么,我们都可以调用基类统一的接口实现他们的功能。使用UML表示如下图所示:

 

 


  角色说明:

  工厂类(Creator):根据业务逻辑创建具体产品,由客户程序直接调用。

  抽象产品(Product):作为具体产品的基类,提供统一的接口,也是工厂类要返回的类型。

  具体产品(Concrete Product):工厂类真正要创建的类型。上图中仅仅展示了一个具体产品,有多个产品的时候类似。

  下面我们对简单工厂模式进行总结。使用简单工厂的好处是:

  1、充分利用了多态性不管什么具体产品都返回抽象产品。
  2、充分利用了封装性,内部产品发生变化时外部使用者不会受到影响。

  缺点是:如果增加了新的产品,就必须得修改工厂(Factory)。

  抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象。这就是抽象工厂模式的用意

  我们将工厂模式推广到一般的情况,它的类图如下所示:

 

 


  在有名的OOD的设计原则中有一个叫做里氏代换原则(Liskov Substitution Principle, LSP)。它的实质也就是讲向上转型。它的内容是:任何接收父类型的地方,都应当能够接收子类型,换句话说如果使用的是一个基类的话,那么一定适用于其子类,而且程序察觉不出基类对象和子类对象的区别。LSP是继承复用的基石,只有当派生类可以替换掉基类,软件的功能不受到影响时,基类才能真正被复用。

 

<think>好的,我需要帮助用户分析简单工厂模式的缺点和局限性。首先,我应该回顾一下用户提供的引用内容,看看里面有没有相关的信息。 引用[1]提到:“缺点:实现复杂,不适合简单的应用场景。对开闭原则的支持也有一定的局限性,当需要增加新的产品对象种类时,需要修改抽象工厂和所有具体工厂的代码。” 这里直接指出了简单工厂模式的缺点,特别是关于开闭原则的问题。需要扩展时,必须修改工厂类,这不符合开闭原则的“对扩展开放,对修改关闭”的要求。 引用[4]是转载的一篇分析简单工厂模式优缺点的文章,可能里面有更详细的说明。虽然具体内容看不到,但根据引用[1]的描述,可以推测它可能讨论了类似的问题,比如工厂类职责过重、扩展困难等。 接下来,我需要整理这些信息,用中文清晰地解释简单工厂模式的缺点和局限性。要确保结构清晰,分点列出,每个点都给出具体的例子或说明,让用户容易理解。同时,要遵循用户给出的系统级指令,比如正确使用LaTeX格式,生成相关问题,并在引用处添加标识。 需要注意的是,用户可能混淆了简单工厂模式和抽象工厂模式,因为引用[3]提到了抽象工厂模式,但用户的问题是关于简单工厂模式的。因此,在回答时要明确区分,避免混淆。 最后,生成的相关问题应该围绕简单工厂模式的缺点展开,例如与其他工厂模式的对比、适用场景等,帮助用户进一步学习。</think>### 简单工厂模式的缺点及局限性分析 简单工厂模式通过集中化的工厂类创建对象,简化了客户端代码,但其核心问题在于**对系统扩展性和设计灵活性的限制**。以下是具体分析: #### 1. **违反开闭原则** 开闭原则要求“对扩展开放,对修改关闭”,但简单工厂模式在新增产品类型时,必须修改工厂类的代码。例如: ```java public class SimpleFactory { public Product createProduct(String type) { if (type.equals("A")) { return new ProductA(); } else if (type.equals("B")) { // 新增产品C时需在此处添加条件分支 return new ProductB(); } return null; } } ``` 每次新增产品类型(如`ProductC`)都需要修改`createProduct`方法,导致工厂类频繁变动[^1][^4]。 #### 2. **工厂类职责过重** 工厂类承担了所有产品的创建逻辑,随着产品种类增多,代码会变得臃肿且难以维护。例如: - 需要处理多个产品族的创建逻辑(如不同品牌的手机) - 包含复杂的条件分支(如根据参数选择不同构造方式) 这会使工厂类成为系统性能瓶颈,并增加单点故障风险[^2]。 #### 3. **类型判断冗余** 客户端需通过字符串或枚举类型标识产品类别,例如: ```python factory.create_product("LED") # 传递字符串参数指定产品类型 ``` 若参数传递错误(如拼写错误),会导致运行时异常而非编译时错误,降低代码健壮性。 #### 4. **难以应对多层级产品结构** 当产品存在多个层级或维度时(如“手机+耳机”组合产品),简单工厂模式无法优雅地扩展。例如: - 需要创建`旗舰手机+降噪耳机`和`入门手机+普通耳机`的组合 - 需在工厂类中硬编码所有组合关系,导致代码复杂度激增 此时更适合使用**抽象工厂模式**,通过多个工厂类分别处理不同产品族[^3]。 #### 5. **单元测试困难** 由于工厂类直接耦合具体产品实现,测试时难以通过**依赖注入**替换为Mock对象。例如: ```java // 测试代码无法轻松替换RealProduct为MockProduct Product obj = SimpleFactory.createProduct("Real"); ``` --- ### 适用场景建议 简单工厂模式适用于以下场景: 1. 产品种类较少且稳定(如不超过5种) 2. 客户端不关心具体创建过程 3. 需要集中化控制对象创建逻辑(如配置化对象生成) 若系统需要频繁扩展新产品类型,建议改用**工厂方法模式**(每个产品对应独立工厂)或**抽象工厂模式**(支持产品族扩展)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值