在我们平常创建对象的时候,都是通过关键字 new 来实现的,例:Class A = new A() 。
在一些情况下,要创建的对象需要一系列复杂的初始化操作,比如查配置文件、查数据库表、初始化成员对象等,如果把这些逻辑放在构造函数中,会极大影响代码的可读性。不妨定义一个类来专门负责对象的创建,这样的类就是工厂类,这种做法就是工厂模式,在任何需要生成复杂对象的地方,都可以使用工厂模式。
工厂模式包括:简单工厂(不在23种设计模式中)、工厂方法和抽象工厂。

简单工厂

定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类。
工厂模式 - 图1

  • 抽象类或接口:定义了要创建的产品对象的接口。
  • 具体实现:具有统一父类的具体类型的产品。
  • 产品工厂:负责创建产品对象。工厂模式同样体现了开闭原则,将“创建具体的产品实现类”这部分变化的代码从不变化的代码“使用产品”中分离出来,之后想要新增产品时,只需要扩展工厂的实现即可。
    1. /*
    2. 一个栗子:
    3. 我喜欢吃面条,抽象一个面条基类,(接口也可以),这是产品的抽象类。
    4. */
    5. abstract class INoodles {
    6. /**
    7. * 描述每种面条啥样的
    8. */
    9. public abstract void desc();
    10. }
    11. /**
    12. *先来一份兰州拉面(具体的产品类):
    13. */
    14. class LzNoodles extends INoodles {
    15. @Override
    16. public void desc() {
    17. System.out.println("兰州拉面 上海的好贵 家里才5 6块钱一碗");
    18. }
    19. }
    20. /**
    21. * 程序员加班必备也要吃泡面(具体的产品类):
    22. * 复制代码
    23. */
    24. class PaoNoodles extends INoodles {
    25. @Override
    26. public void desc() {
    27. System.out.println("泡面好吃 可不要贪杯");
    28. }
    29. }
    30. /**
    31. * 还有我最爱吃的家乡的干扣面(具体的产品类):
    32. * 复制代码
    33. */
    34. class GankouNoodles extends INoodles {
    35. @Override
    36. public void desc() {
    37. System.out.println("还是家里的干扣面好吃 6块一碗");
    38. }
    39. }
    40. /**
    41. * 太和版面
    42. */
    43. class TaihePanmian extends INoodles {
    44. @Override
    45. public void desc() {
    46. System.out.println("太和板面");
    47. }
    48. }
    49. /**
    50. * 简单工厂模式
    51. */
    52. class SimpleNoodlesFactory {
    53. private static final int TYPE_LZ = 1;//兰州拉面
    54. private static final int TYPE_PM = 2;//泡面
    55. private static final int TYPE_GK = 3;//干扣面
    56. private static final int TYPE_TH = 4;//太和板面
    57. INoodles createNoodles(int type) {
    58. switch (type) {
    59. case TYPE_LZ:
    60. return new LzNoodles();
    61. case TYPE_PM:
    62. return new PaoNoodles();
    63. case TYPE_GK:
    64. return new GankouNoodles();
    65. case TYPE_TH:
    66. return new TaihePanmian();
    67. default:
    68. return null;
    69. }
    70. }
    71. }
    72. public class test {
    73. public static void main(String[] args) {
    74. SimpleNoodlesFactory simpleNoodlesFactory=new SimpleNoodlesFactory();
    75. INoodles iNoodles;
    76. iNoodles=simpleNoodlesFactory.createNoodles(2);
    77. iNoodles.desc();
    78. iNoodles=simpleNoodlesFactory.createNoodles(4);
    79. iNoodles.desc();
    80. }
    81. }

    特点

    1 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。
    2 create()方法通常是静态的,所以也称之为静态工厂。

    缺点

    1 扩展性差(我想增加一种面条,除了新增一个面条产品类,还需要修改工厂类方法)
    2 不同的产品需要不同额外参数的时候 不支持。

    工厂方法模式

    1.模式描述

    提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例。

    2.模式作用

    可以一定程度上解耦,消费者和产品实现类隔离开,只依赖产品接口(抽象产品),产品实现类如何改动与消费者完全无关。
    可以一定程度增加扩展性,若增加一个产品实现,只需要实现产品接口,修改工厂创建产品的方法,消费者可以无感知(若消费者不关心具体产品是什么的情况)。
    可以一定程度增加代码的封装性、可读性。清楚的代码结构,对于消费者来说很少的代码量就可以完成很多工作。
    等等。//TODO
    另外,抽象工厂才是实际意义的工厂模式,工厂方法只是抽象工厂的一个比较常见的情况。

为了解决上面提到的”增加if-else”的问题,可以为每一个键盘子类建立一个对应的工厂子类,这些工厂子类实现同一个抽象工厂接口。这样,创建不同品牌的键盘,只需要实现不同的工厂子类。当有新品牌加入时,新建具体工厂继承抽象工厂,而不用修改任何一个类。

结构

工厂模式 - 图2

  • 抽象工厂:声明了工厂方法的接口。
  • 具体产品工厂:实现工厂方法的接口,负责创建产品对象。
  • 产品抽象类或接口:定义工厂方法所创建的产品对象的接口。
  • 具体产品实现:具有统一父类的具体类型的产品。 ``` / 一个栗子: 我喜欢吃面条,抽象一个面条基类,(接口也可以),这是产品的抽象类。 / abstract class INoodles { /**
    • 描述每种面条啥样的 */ public abstract void desc(); }

/ 先来一份兰州拉面(具体的产品类): / class LzNoodles extends INoodles { @Override public void desc() { System.out.println(“兰州拉面 上海的好贵 家里才5 6块钱一碗”); } } /

  • 太和版面 */

class TaihePanmian extends INoodles { @Override public void desc() { System.out.println(“太和板面”); } } /**

  • 工厂类的接口 / interface INoodlesFactory { INoodles createProduct(); } /*
  • 兰州拉面工厂 */ class LzNoodlesFactory implements INoodlesFactory{

    @Override public INoodles createProduct() {

     return new LzNoodles();
    

    } } /**

  • 泰和工厂 */ class TaiHeNoodlesFactory implements INoodlesFactory{

    @Override public INoodles createProduct() {

     return new TaihePanmian();
    

    } }

public class test { public static void main(String[] args) { INoodlesFactory iNoodlesFactory=new TaiHeNoodlesFactory(); INoodles product= iNoodlesFactory.createProduct(); product.desc(); iNoodlesFactory=new LzNoodlesFactory(); product=iNoodlesFactory.createProduct(); product.desc(); } }

<a name="EFzQh"></a>
## **抽象工厂**


为了缩减工厂实现子类的数量,不必给每一个产品分配一个工厂类,可以将产品进行分组,每组中的不同产品由同一个工厂类的不同方法来创建。<br />例如,键盘、主机这2种产品可以分到同一个分组——电脑,而不同品牌的电脑由不同的制造商工厂来创建。<br />![](https://cdn.nlark.com/yuque/0/2021/jpeg/2573838/1624116490134-34967762-fe37-4eab-97fa-cb43cf354515.jpeg#height=297&id=i0NvF&originHeight=297&originWidth=720&originalType=binary&ratio=1&size=0&status=done&style=none&width=720)<br />类似这种把产品类分组,组内不同产品由同一工厂类的不同方法实现的设计模式,就是抽象工厂模式。<br />抽象工厂适用于以下情况:<br />1. 一个系统要独立于它的产品的创建、组合和表示时;<br />2. 一个系统要由多个产品系列中的一个来配置时;<br />3. 要强调一系列相关的产品对象的设计以便进行联合使用时;<br />4. 当你提供一个产品类库,而只想显示它们的接口而不是实现时;
<a name="0343f30d"></a>
### 抽象工厂模式与工厂方法模式的区别
       抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。<br />        在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。
<a name="4mXFK"></a>
###  结构
![](https://cdn.nlark.com/yuque/0/2021/jpeg/2573838/1624116490418-68eccc84-fc8a-4d2a-9e07-78f8836dea46.jpeg#height=487&id=Rje77&originHeight=487&originWidth=720&originalType=binary&ratio=1&size=0&status=done&style=none&width=720)

- 抽象工厂:声明了创建抽象产品对象的操作接口。
- 具体产品工厂:实现了抽象工厂的接口,负责创建产品对象。
- 产品抽象类或接口:定义一类产品对象的接口。
- 具体产品实现:定义一个将被相应具体工厂创建的产品对象。

interface Keyboard { void print(); } class DellKeyboard implements Keyboard { @Override public void print() { //…dell…dell; System.out.println(“dell 键盘”); } } class HPKeyboard implements Keyboard { @Override public void print() { //…HP…HP; System.out.println(“hp 键盘”); } } interface Monitor { void play(); } class DellMonitor implements Monitor { @Override public void play() { //…dell…dell; System.out.println(“dell play”); } } class HPMonitor implements Monitor { @Override public void play() { //…HP…HP; System.out.println(“Hp display”); } } interface MainFrame { void run(); } class DellMainFrame implements MainFrame { @Override public void run() { //…dell…dell; System.out.println(“dell 框架”); } } class HPMainFrame implements MainFrame { @Override public void run() { //…HP…HP; System.out.println(“hp 框架”);

}

} //工厂类。工厂分为Dell工厂和HP工厂,各自负责品牌内产品的创建 interface IFactory { MainFrame createMainFrame(); Monitor createMonitor(); Keyboard createKeyboard(); } class DellFactory implements IFactory { @Override public MainFrame createMainFrame(){ MainFrame mainFrame = new DellMainFrame(); //…造一个Dell主机; return mainFrame; } @Override public Monitor createMonitor(){ Monitor monitor = new DellMonitor(); //…造一个Dell显示器; return monitor; } @Override public Keyboard createKeyboard(){ Keyboard keyboard = new DellKeyboard(); //…造一个Dell键盘; return keyboard; } } class HPFactory implements IFactory { @Override public MainFrame createMainFrame(){ MainFrame mainFrame = new HPMainFrame(); //…造一个HP主机; return mainFrame; } @Override public Monitor createMonitor(){ Monitor monitor = new HPMonitor(); //…造一个HP显示器; return monitor; } @Override public Keyboard createKeyboard(){ Keyboard keyboard = new HPKeyboard(); //…造一个HP键盘; return keyboard; } } //客户端代码。实例化不同的工厂子类,可以通过不同的创建方法创建不同的产品 public class test { public static void main(String[] args) { IFactory dellFactory = new DellFactory(); IFactory hPFactory = new HPFactory(); //创建戴尔 Keyboard keyboard = dellFactory.createKeyboard(); MainFrame mainFrame=dellFactory.createMainFrame(); Monitor monitor=dellFactory.createMonitor(); keyboard.print(); mainFrame.run(); monitor.play(); //创建惠普 keyboard = hPFactory.createKeyboard(); mainFrame=hPFactory.createMainFrame(); monitor=hPFactory.createMonitor(); keyboard.print(); mainFrame.run(); monitor.play(); } } ```

优缺点

增加分组非常简单,例如要增加Lenovo分组,只需创建Lenovo工厂和具体的产品实现类。分组中的产品扩展非常困难,要增加一个鼠标Mouse,既要创建抽象的Mouse接口, 又要增加具体的实现:DellMouse、HPMouse, 还要再每个Factory中定义创建鼠标的方法实现。

总结

  • 简单工厂:唯一工厂类,一个产品抽象类,工厂类的创建方法依据入参判断并创建具体产品对象。
  • 工厂方法:多个工厂类,一个产品抽象类,利用多态创建不同的产品对象,避免了大量的if-else判断。
  • 抽象工厂:多个工厂类,多个产品抽象类,产品子类分组,同一个工厂实现类创建同组中的不同产品,减少了工厂子类的数量。

在下述情况下可以考虑使用工厂模式:

  1. 在编码时不能预见需要创建哪种类的实例。
  2. 系统不应依赖于产品类实例如何被创建、组合和表达的细节。

    总结

    无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。
    所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。