[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"site-settings":3,"\u002Farticles\u002Fdesign-pattern-03":26,"\u002Farticles\u002Fdesign-pattern-03\u002Frelated":43,"\u002Farticles\u002Fdesign-pattern-03\u002Fadjacent":72,"\u002Fcategories":76,"render-design-pattern-03":102},{"code":4,"message":5,"data":6},0,"ok",{"author_city":7,"author_years":8,"site_tagline":9,"site_description":10,"skill_one_desc":11,"author_status":12,"skill_two_title":13,"about_hero_image":9,"skill_one_title":14,"icp":15,"author_role":16,"og_default_image":9,"closing_quote_date":17,"toolkit":18,"skill_two_desc":19,"author_bio":9,"site_footer":9,"site_url":20,"author_tagline":21,"weekly_thought":9,"tagline_line_one":22,"author_bio_two":9,"closing_quote":23,"site_name":24,"tagline_line_two":25,"home_hero_image":9},"西安","10","","花开空白 - 个人博客","熟悉Java、Python、Go等后端语言。","努力工作中","前端开发","后端开发","陕ICP备2021009682号-1","开发者","2022年·秋","Java,JavaScript,TypeScript,Python,Go","熟悉Js、Ts语言以及Vue、uniapp等框架。","https:\u002F\u002Firule.cn","很高兴遇见你。","把复杂的事，","把每次一发布，都当成一次和陌生人的握手。","花开空白","写成简单的故事。",{"code":4,"message":5,"data":27},{"id":28,"slug":29,"title":30,"summary":31,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":34,"likeCount":4,"wordCount":35,"readingMinutes":36,"publishedAt":37,"contentMd":38,"contentHtml":39,"tags":40,"metaTitle":9,"metaDescription":9,"keywords":9,"canonicalUrl":9,"robots":41,"ogImage":9,"faq":42,"aiSummary":42},"2113931131085656066","design-pattern-03","设计模式修炼之路 ③ 工厂模式——简单工厂、工厂方法、抽象工厂三兄弟","简单工厂、工厂方法、抽象工厂三兄弟的演进与取舍、开闭原则得失","2113897628365230081",1,3741,6415,17,"2023-10-13T21:36:05","上一篇《设计模式修炼之路-单例模式》里，我们折腾了六种写法，本质上都是在解决一个问题：**一个类在整个应用里只允许存在一个实例**。单例模式管的是\"有几个\"，而这一篇要聊的工厂模式，管的是另一个同样高频的问题：**实例从哪来**。\n\n`new Circle()` 谁都会写，但当 `new` 这个关键字散落在代码的各个角落时，麻烦就来了：一旦 Circle 的构造过程变了（比如要初始化依赖、要读配置、要打日志），所有 `new Circle()` 的地方都得跟着改。工厂模式的思路很简单：把\"造对象\"这件事收拢到一个专门的地方去，**调用方只管\"要\"，不管\"造\"**。\n\n## 一、什么是工厂模式？\n\n工厂模式（Factory Pattern）是 Java 中最常用的设计模式之一，属于**创建型模式**。它的核心思想用一句话就能说清楚：\n\n> 把对象的创建过程封装到一个\"工厂\"类中，调用方只需要告诉工厂\"我要什么\"，完全不关心对象是怎么被造出来的。\n\n拿去餐厅吃饭类比再合适不过：你坐下来跟服务员说\"来一份宫保鸡丁\"，十分钟后菜就端上来了。你不需要知道厨师是谁、厨房在哪、鸡丁是先焯水还是先过油——**厨房就是工厂，你就是调用方，菜单就是你们之间的接口**。哪天厨师换了做法，甚至整个后厨翻新，只要端上来的还是那盘宫保鸡丁，你完全无感。\n\n平时我们嘴上说的\"工厂模式\"，其实是三兄弟的统称：\n\n- **简单工厂（Simple Factory）**：严格来说它并不属于 GoF 的 23 种设计模式，但它是理解工厂模式的敲门砖——一个工厂包揽所有产品的创建，要什么传什么参数。\n- **工厂方法（Factory Method）**：GoF 23 种之一。一个工厂只生产一种产品，想新增产品？再开一家工厂。\n- **抽象工厂（Abstract Factory）**：GoF 23 种之一。一个工厂生产一整个\"产品族\"（一组相关的产品），连工厂本身都是被\"生产\"出来的。\n\n三兄弟是逐步演进的关系：简单工厂图省事，工厂方法补上开闭原则的漏洞，抽象工厂专门对付产品族。它们的具体区别放在第四节按顺序实现，实现完自然就懂了。\n\n## 二、工厂模式的优缺点\n\n### 优点\n\n1. **调用方和具体类解耦**。调用方依赖的是工厂和抽象接口，而不是一堆具体实现类，具体类内部怎么变都不影响调用方。\n2. **创建逻辑收口**。对象的创建过程集中在一处，想在创建时加缓存、打日志、读配置，都只需要改工厂一个地方。\n3. **符合开闭原则**（工厂方法模式）。新增一种产品时，只需要新增对应的产品类和工厂类，已有代码一行不动——上一篇提过的开闭原则，工厂方法是教科书级的示范。\n\n### 缺点\n\n1. **类的数量膨胀**。工厂方法模式下每种产品都要配一个工厂类，产品一多，类文件成倍增加，代码结构变复杂。\n2. **简单工厂违背开闭原则**。新增产品就得改工厂类里的判断逻辑，\"对扩展开放，对修改关闭\"直接破防。\n3. **抽象工厂纵向扩展难**。产品族里想新增一条全新的产品线，抽象工厂接口一改，所有具体工厂都得跟着改，牵一发动全身。\n\n可以看出，三种形态各有各的取舍，没有银弹——这也是设计模式一贯的画风：**用复杂度换取扩展性**。\n\n## 三、工厂模式的应用场景\n\n工厂模式太常见了，常见到你天天在用却未必意识到：\n\n- **JDK 里**：`Calendar.getInstance()`——你拿到的是 Calendar 的子类实例（比如 GregorianCalendar），但从来没 `new` 过它；`Integer.valueOf()` 也是类似的思路，还顺带做了缓存。\n- **Spring 里**：`BeanFactory`——整个 IoC 容器就是一个超级工厂，`getBean()` 就是工厂方法的放大版，你所有的 Bean 都是从这个工厂里\"造\"出来的。\n- **JDBC 里**：`DriverManager.getConnection()`——数据库连接的创建细节被完全屏蔽，换数据库只是换了个\"供应商\"。\n\n**什么时候该用**：当对象的创建过程比较复杂（依赖多、要初始化、要读配置），或者调用方只关心\"能用\"而不关心\"怎么造\"，或者同一族产品可能频繁增删时，就该考虑把创建逻辑收进工厂了。反过来，如果就是简单 `new` 一下的事，硬套工厂模式纯属过度设计。\n\n## 四、工厂模式的实现方式\n\n老规矩，光说不练假把式。本篇的示例代码按模式分包放在 design-pattern 项目里，目录结构如下：\n\n```\ndesign-pattern\u002Fsrc\u002Fmain\u002Fjava\n├── singleton                     \u002F\u002F 上一篇单例模式的六种实现\n├── factory                       \u002F\u002F 工厂模式的公共产品\n│   ├── Shape.java                \u002F\u002F 图形产品接口\n│   ├── Circle.java \u002F Rectangle.java \u002F Square.java\n│   ├── simple                    \u002F\u002F 简单工厂\n│   ├── method                    \u002F\u002F 工厂方法\n│   └── abstractfactory           \u002F\u002F 抽象工厂\n└── Test.java                     \u002F\u002F 测试入口\n```\n\n需求场景很简单：画图形。三种图形都实现 Shape 接口：\n\n```java\npublic interface Shape {\n    void draw();\n}\n```\n\n```java\npublic class Circle implements Shape {\n\n    public Circle() {\n        System.out.println(\"创建了 Circle 对象\");\n    }\n\n    @Override\n    public void draw() {\n        System.out.println(\"画了一个圆形\");\n    }\n}\n```\n\nRectangle 和 Square 与之类似，不再重复贴。构造函数里特意加了一句打印，是为了从运行结果里看清对象到底是什么时候被创建的。\n\n### 1. 简单工厂\n\n简单工厂的思路最直白：开一家工厂，客户报什么名字，工厂就造什么。\n\n```java\npublic class ShapeFactory {\n\n    public static Shape getShape(String shapeType) {\n        if (shapeType == null) {\n            return null;\n        }\n        if (shapeType.equalsIgnoreCase(\"CIRCLE\")) {\n            return new Circle();\n        } else if (shapeType.equalsIgnoreCase(\"RECTANGLE\")) {\n            return new Rectangle();\n        } else if (shapeType.equalsIgnoreCase(\"SQUARE\")) {\n            return new Square();\n        }\n        return null;\n    }\n}\n```\n\n测试代码（因为项目里有三个同名的 ShapeFactory，这里用全限定名调用，实际写代码时 import 对应包即可）：\n\n```java\nprivate static void simpleFactory() {\n    Shape circle = factory.simple.ShapeFactory.getShape(\"CIRCLE\");\n    Shape rectangle = factory.simple.ShapeFactory.getShape(\"RECTANGLE\");\n    Shape square = factory.simple.ShapeFactory.getShape(\"SQUARE\");\n    circle.draw();\n    rectangle.draw();\n    square.draw();\n}\n```\n\n运行结果：\n\n```\n创建了 Circle 对象\n创建了 Rectangle 对象\n创建了 Square 对象\n画了一个圆形\n画了一个长方形\n画了一个正方形\n```\n\n调用方拿到的都是 Shape 类型，全程没出现一个 `new Circle()`，创建逻辑确实被收口了。但它的问题也很明显：**想新增一种\"三角形\"，就得回来改 getShape() 的 if-else**，修改了已经存在的类，开闭原则就此失守——这也是它没能进 GoF 23 种名单的原因。\n\n顺带一提，简单工厂还有一种进阶写法：用反射 `Class.forName(shapeType).getDeclaredConstructor().newInstance()` 把 if-else 干掉。能跑，但失去了编译期类型检查，参数传错要拖到运行时才炸，实际项目里并不推荐。\n\n### 2. 工厂方法\n\n简单工厂的病根在于\"一个工厂包办所有产品\"，工厂方法模式的药方就是：**把工厂也抽象出来，一个工厂只负责一种产品**。\n\n```java\npublic interface ShapeFactory {\n    Shape getShape();\n}\n```\n\n```java\npublic class CircleFactory implements ShapeFactory {\n\n    @Override\n    public Shape getShape() {\n        return new Circle();\n    }\n}\n```\n\nRectangleFactory、SquareFactory 同理。测试代码：\n\n```java\nprivate static void factoryMethod() {\n    ShapeFactory circleFactory = new CircleFactory();\n    ShapeFactory rectangleFactory = new RectangleFactory();\n    ShapeFactory squareFactory = new SquareFactory();\n    circleFactory.getShape().draw();\n    rectangleFactory.getShape().draw();\n    squareFactory.getShape().draw();\n}\n```\n\n运行结果：\n\n```\n创建了 Circle 对象\n画了一个圆形\n创建了 Rectangle 对象\n画了一个长方形\n创建了 Square 对象\n画了一个正方形\n```\n\n对比简单工厂的运行结果会发现一个有趣的细节：这次\"创建\"和\"画图\"是交替出现的，而简单工厂是先一口气全创建好再统一画。倒不是两种模式本身有这个区别，而是工厂方法交给调用方的是**工厂对象**，产品什么时候造、造几个，主动权完全在调用方手里——这就是把\"创建能力\"延迟交付的好处。\n\n现在想新增三角形？新建一个 Triangle 类、一个 TriangleFactory 类，就能接入使用了，**已有代码一行不动**，开闭原则保住了。代价也摆在那里：三种产品配了三个工厂类，类数量直接翻倍。\n\n### 3. 抽象工厂\n\n前两种工厂都只管一条产品线，但现实需求里经常是**一族产品**搭配出现：画图程序里除了图形还有颜色，图形和颜色就是两条产品线。抽象工厂模式让\"工厂\"本身也变成了被创建的对象——由一个超级工厂（工厂的工厂）来生产具体的工厂。\n\n先加一条颜色产品线：\n\n```java\npublic interface Color {\n    void fill();\n}\n```\n\n```java\npublic class Red implements Color {\n\n    public Red() {\n        System.out.println(\"创建了 Red 对象\");\n    }\n\n    @Override\n    public void fill() {\n        System.out.println(\"填充了红色\");\n    }\n}\n```\n\nGreen、Blue 同理。然后是抽象工厂接口，注意它同时声明了两条产品线的创建能力：\n\n```java\npublic interface AbstractFactory {\n    Shape getShape(String shapeType);\n\n    Color getColor(String colorType);\n}\n```\n\n具体工厂各自负责一条产品线，造不了的产品类型返回 null：\n\n```java\npublic class ShapeFactory implements AbstractFactory {\n\n    @Override\n    public Shape getShape(String shapeType) {\n        if (shapeType == null) {\n            return null;\n        }\n        if (shapeType.equalsIgnoreCase(\"CIRCLE\")) {\n            return new Circle();\n        } else if (shapeType.equalsIgnoreCase(\"RECTANGLE\")) {\n            return new Rectangle();\n        } else if (shapeType.equalsIgnoreCase(\"SQUARE\")) {\n            return new Square();\n        }\n        return null;\n    }\n\n    @Override\n    public Color getColor(String colorType) {\n        return null;\n    }\n}\n```\n\nColorFactory 则相反：getColor() 里做 RED\u002FGREEN\u002FBLUE 的判断，getShape() 返回 null。最后是\"工厂的工厂\"：\n\n```java\npublic class FactoryProducer {\n\n    public static AbstractFactory getFactory(String factoryType) {\n        if (factoryType == null) {\n            return null;\n        }\n        if (factoryType.equalsIgnoreCase(\"SHAPE\")) {\n            return new ShapeFactory();\n        } else if (factoryType.equalsIgnoreCase(\"COLOR\")) {\n            return new ColorFactory();\n        }\n        return null;\n    }\n}\n```\n\n测试代码：\n\n```java\nprivate static void abstractFactory() {\n    AbstractFactory shapeFactory = FactoryProducer.getFactory(\"SHAPE\");\n    Shape circle = shapeFactory.getShape(\"CIRCLE\");\n    Shape square = shapeFactory.getShape(\"SQUARE\");\n    circle.draw();\n    square.draw();\n    AbstractFactory colorFactory = FactoryProducer.getFactory(\"COLOR\");\n    Color red = colorFactory.getColor(\"RED\");\n    Color green = colorFactory.getColor(\"GREEN\");\n    red.fill();\n    green.fill();\n}\n```\n\n运行结果：\n\n```\n创建了 Circle 对象\n创建了 Square 对象\n画了一个圆形\n画了一个正方形\n创建了 Red 对象\n创建了 Green 对象\n填充了红色\n填充了绿色\n```\n\n注意 ColorFactory 里 getShape() 返回的是 null——抽象工厂的强项是**同一条产品线内的横向扩展**：新增一种颜色，只需要改 ColorFactory 一处；但要是想新增一条全新的产品线（比如\"文字\"），AbstractFactory 接口一改，ShapeFactory、ColorFactory 所有具体工厂全部得跟着改。一句话总结：**横向（同族加产品）容易，纵向（加产品线）疼**。\n\n## 五、三种工厂怎么选\n\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>\u003C\u002Fth>\u003Cth>简单工厂\u003C\u002Fth>\u003Cth>工厂方法\u003C\u002Fth>\u003Cth>抽象工厂\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\u003Ctr>\u003Ctd>是否属于 GoF 23 种\u003C\u002Ftd>\u003Ctd>否\u003C\u002Ftd>\u003Ctd>是\u003C\u002Ftd>\u003Ctd>是\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>工厂与产品的关系\u003C\u002Ftd>\u003Ctd>一个工厂造所有产品\u003C\u002Ftd>\u003Ctd>一个工厂造一种产品\u003C\u002Ftd>\u003Ctd>一个工厂造一族产品\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>新增同类产品\u003C\u002Ftd>\u003Ctd>修改工厂代码 ✗\u003C\u002Ftd>\u003Ctd>新增类即可 ✓\u003C\u002Ftd>\u003Ctd>同族内新增类即可 ✓\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>新增产品线\u003C\u002Ftd>\u003Ctd>修改工厂代码 ✗\u003C\u002Ftd>\u003Ctd>新增一套工厂接口\u003C\u002Ftd>\u003Ctd>修改抽象工厂接口 ✗\u003C\u002Ftd>\u003C\u002Ftr>\u003Ctr>\u003Ctd>类的数量\u003C\u002Ftd>\u003Ctd>少\u003C\u002Ftd>\u003Ctd>多\u003C\u002Ftd>\u003Ctd>多\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\n\n实际开发里的选型建议很朴素：**产品少且稳定，用简单工厂，图个轻量；产品会持续增加，用工厂方法，守住开闭原则；产品天然成族、需要保证同一族产品搭配使用时，再上抽象工厂**。绝大多数业务代码，一个简单工厂（或者简单工厂加策略模式）就够用了，别为了模式而模式。\n\n## 写在最后\n\n回顾一下这一篇：工厂模式把\"造对象\"从调用方手里收走，调用方从此只关心\"要什么\"。简单工厂一把梭但违背开闭原则；工厂方法一个产品一个工厂，类多了但扩展稳了；抽象工厂管住整个产品族，横向扩展容易、纵向扩展头疼。三种形态没有高下之分，只有取舍之别——而这，正是所有设计模式的共同底色。\n\n示例代码都放在 design-pattern 项目里，按模式分了包（singleton、factory.simple、factory.method、factory.abstractfactory），和上一篇的单例代码放在同一个项目下，方便对照练习。\n\n下一篇我们继续创建型模式，聊聊另一种\"造对象\"的思路——**建造者模式（Builder）**：当一个对象的构造参数多到 `new Pizza(10, true, false, null, \"BIG\")` 这种程度时，该怎么优雅地把它造出来。","\u003Cp>上一篇《设计模式修炼之路-单例模式》里，我们折腾了六种写法，本质上都是在解决一个问题：\u003Cstrong>一个类在整个应用里只允许存在一个实例\u003C\u002Fstrong>。单例模式管的是\"有几个\"，而这一篇要聊的工厂模式，管的是另一个同样高频的问题：\u003Cstrong>实例从哪来\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>\u003Ccode>new Circle()\u003C\u002Fcode> 谁都会写，但当 \u003Ccode>new\u003C\u002Fcode> 这个关键字散落在代码的各个角落时，麻烦就来了：一旦 Circle 的构造过程变了（比如要初始化依赖、要读配置、要打日志），所有 \u003Ccode>new Circle()\u003C\u002Fcode> 的地方都得跟着改。工厂模式的思路很简单：把\"造对象\"这件事收拢到一个专门的地方去，\u003Cstrong>调用方只管\"要\"，不管\"造\"\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch2>一、什么是工厂模式？\u003C\u002Fh2>\n\u003Cp>工厂模式（Factory Pattern）是 Java 中最常用的设计模式之一，属于\u003Cstrong>创建型模式\u003C\u002Fstrong>。它的核心思想用一句话就能说清楚：\u003C\u002Fp>\n\u003Cblockquote>\n \u003Cp>把对象的创建过程封装到一个\"工厂\"类中，调用方只需要告诉工厂\"我要什么\"，完全不关心对象是怎么被造出来的。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>拿去餐厅吃饭类比再合适不过：你坐下来跟服务员说\"来一份宫保鸡丁\"，十分钟后菜就端上来了。你不需要知道厨师是谁、厨房在哪、鸡丁是先焯水还是先过油——\u003Cstrong>厨房就是工厂，你就是调用方，菜单就是你们之间的接口\u003C\u002Fstrong>。哪天厨师换了做法，甚至整个后厨翻新，只要端上来的还是那盘宫保鸡丁，你完全无感。\u003C\u002Fp>\n\u003Cp>平时我们嘴上说的\"工厂模式\"，其实是三兄弟的统称：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>简单工厂（Simple Factory）\u003C\u002Fstrong>：严格来说它并不属于 GoF 的 23 种设计模式，但它是理解工厂模式的敲门砖——一个工厂包揽所有产品的创建，要什么传什么参数。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>工厂方法（Factory Method）\u003C\u002Fstrong>：GoF 23 种之一。一个工厂只生产一种产品，想新增产品？再开一家工厂。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>抽象工厂（Abstract Factory）\u003C\u002Fstrong>：GoF 23 种之一。一个工厂生产一整个\"产品族\"（一组相关的产品），连工厂本身都是被\"生产\"出来的。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>三兄弟是逐步演进的关系：简单工厂图省事，工厂方法补上开闭原则的漏洞，抽象工厂专门对付产品族。它们的具体区别放在第四节按顺序实现，实现完自然就懂了。\u003C\u002Fp>\n\u003Ch2>二、工厂模式的优缺点\u003C\u002Fh2>\n\u003Ch3>优点\u003C\u002Fh3>\n\u003Col>\n \u003Cli>\u003Cstrong>调用方和具体类解耦\u003C\u002Fstrong>。调用方依赖的是工厂和抽象接口，而不是一堆具体实现类，具体类内部怎么变都不影响调用方。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>创建逻辑收口\u003C\u002Fstrong>。对象的创建过程集中在一处，想在创建时加缓存、打日志、读配置，都只需要改工厂一个地方。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>符合开闭原则\u003C\u002Fstrong>（工厂方法模式）。新增一种产品时，只需要新增对应的产品类和工厂类，已有代码一行不动——上一篇提过的开闭原则，工厂方法是教科书级的示范。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>缺点\u003C\u002Fh3>\n\u003Col>\n \u003Cli>\u003Cstrong>类的数量膨胀\u003C\u002Fstrong>。工厂方法模式下每种产品都要配一个工厂类，产品一多，类文件成倍增加，代码结构变复杂。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>简单工厂违背开闭原则\u003C\u002Fstrong>。新增产品就得改工厂类里的判断逻辑，\"对扩展开放，对修改关闭\"直接破防。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>抽象工厂纵向扩展难\u003C\u002Fstrong>。产品族里想新增一条全新的产品线，抽象工厂接口一改，所有具体工厂都得跟着改，牵一发动全身。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>可以看出，三种形态各有各的取舍，没有银弹——这也是设计模式一贯的画风：\u003Cstrong>用复杂度换取扩展性\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch2>三、工厂模式的应用场景\u003C\u002Fh2>\n\u003Cp>工厂模式太常见了，常见到你天天在用却未必意识到：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>JDK 里\u003C\u002Fstrong>：\u003Ccode>Calendar.getInstance()\u003C\u002Fcode>——你拿到的是 Calendar 的子类实例（比如 GregorianCalendar），但从来没 \u003Ccode>new\u003C\u002Fcode> 过它；\u003Ccode>Integer.valueOf()\u003C\u002Fcode> 也是类似的思路，还顺带做了缓存。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>Spring 里\u003C\u002Fstrong>：\u003Ccode>BeanFactory\u003C\u002Fcode>——整个 IoC 容器就是一个超级工厂，\u003Ccode>getBean()\u003C\u002Fcode> 就是工厂方法的放大版，你所有的 Bean 都是从这个工厂里\"造\"出来的。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>JDBC 里\u003C\u002Fstrong>：\u003Ccode>DriverManager.getConnection()\u003C\u002Fcode>——数据库连接的创建细节被完全屏蔽，换数据库只是换了个\"供应商\"。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>什么时候该用\u003C\u002Fstrong>：当对象的创建过程比较复杂（依赖多、要初始化、要读配置），或者调用方只关心\"能用\"而不关心\"怎么造\"，或者同一族产品可能频繁增删时，就该考虑把创建逻辑收进工厂了。反过来，如果就是简单 \u003Ccode>new\u003C\u002Fcode> 一下的事，硬套工厂模式纯属过度设计。\u003C\u002Fp>\n\u003Ch2>四、工厂模式的实现方式\u003C\u002Fh2>\n\u003Cp>老规矩，光说不练假把式。本篇的示例代码按模式分包放在 design-pattern 项目里，目录结构如下：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>design-pattern\u002Fsrc\u002Fmain\u002Fjava\n├── singleton                     \u002F\u002F 上一篇单例模式的六种实现\n├── factory                       \u002F\u002F 工厂模式的公共产品\n│   ├── Shape.java                \u002F\u002F 图形产品接口\n│   ├── Circle.java \u002F Rectangle.java \u002F Square.java\n│   ├── simple                    \u002F\u002F 简单工厂\n│   ├── method                    \u002F\u002F 工厂方法\n│   └── abstractfactory           \u002F\u002F 抽象工厂\n└── Test.java                     \u002F\u002F 测试入口\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>需求场景很简单：画图形。三种图形都实现 Shape 接口：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public interface Shape {\n    void draw();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-java\">public class Circle implements Shape {\n\n    public Circle() {\n        System.out.println(\"创建了 Circle 对象\");\n    }\n\n    @Override\n    public void draw() {\n        System.out.println(\"画了一个圆形\");\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Rectangle 和 Square 与之类似，不再重复贴。构造函数里特意加了一句打印，是为了从运行结果里看清对象到底是什么时候被创建的。\u003C\u002Fp>\n\u003Ch3>1. 简单工厂\u003C\u002Fh3>\n\u003Cp>简单工厂的思路最直白：开一家工厂，客户报什么名字，工厂就造什么。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class ShapeFactory {\n\n    public static Shape getShape(String shapeType) {\n        if (shapeType == null) {\n            return null;\n        }\n        if (shapeType.equalsIgnoreCase(\"CIRCLE\")) {\n            return new Circle();\n        } else if (shapeType.equalsIgnoreCase(\"RECTANGLE\")) {\n            return new Rectangle();\n        } else if (shapeType.equalsIgnoreCase(\"SQUARE\")) {\n            return new Square();\n        }\n        return null;\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>测试代码（因为项目里有三个同名的 ShapeFactory，这里用全限定名调用，实际写代码时 import 对应包即可）：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">private static void simpleFactory() {\n    Shape circle = factory.simple.ShapeFactory.getShape(\"CIRCLE\");\n    Shape rectangle = factory.simple.ShapeFactory.getShape(\"RECTANGLE\");\n    Shape square = factory.simple.ShapeFactory.getShape(\"SQUARE\");\n    circle.draw();\n    rectangle.draw();\n    square.draw();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>创建了 Circle 对象\n创建了 Rectangle 对象\n创建了 Square 对象\n画了一个圆形\n画了一个长方形\n画了一个正方形\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>调用方拿到的都是 Shape 类型，全程没出现一个 \u003Ccode>new Circle()\u003C\u002Fcode>，创建逻辑确实被收口了。但它的问题也很明显：\u003Cstrong>想新增一种\"三角形\"，就得回来改 getShape() 的 if-else\u003C\u002Fstrong>，修改了已经存在的类，开闭原则就此失守——这也是它没能进 GoF 23 种名单的原因。\u003C\u002Fp>\n\u003Cp>顺带一提，简单工厂还有一种进阶写法：用反射 \u003Ccode>Class.forName(shapeType).getDeclaredConstructor().newInstance()\u003C\u002Fcode> 把 if-else 干掉。能跑，但失去了编译期类型检查，参数传错要拖到运行时才炸，实际项目里并不推荐。\u003C\u002Fp>\n\u003Ch3>2. 工厂方法\u003C\u002Fh3>\n\u003Cp>简单工厂的病根在于\"一个工厂包办所有产品\"，工厂方法模式的药方就是：\u003Cstrong>把工厂也抽象出来，一个工厂只负责一种产品\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public interface ShapeFactory {\n    Shape getShape();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-java\">public class CircleFactory implements ShapeFactory {\n\n    @Override\n    public Shape getShape() {\n        return new Circle();\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>RectangleFactory、SquareFactory 同理。测试代码：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">private static void factoryMethod() {\n    ShapeFactory circleFactory = new CircleFactory();\n    ShapeFactory rectangleFactory = new RectangleFactory();\n    ShapeFactory squareFactory = new SquareFactory();\n    circleFactory.getShape().draw();\n    rectangleFactory.getShape().draw();\n    squareFactory.getShape().draw();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>创建了 Circle 对象\n画了一个圆形\n创建了 Rectangle 对象\n画了一个长方形\n创建了 Square 对象\n画了一个正方形\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>对比简单工厂的运行结果会发现一个有趣的细节：这次\"创建\"和\"画图\"是交替出现的，而简单工厂是先一口气全创建好再统一画。倒不是两种模式本身有这个区别，而是工厂方法交给调用方的是\u003Cstrong>工厂对象\u003C\u002Fstrong>，产品什么时候造、造几个，主动权完全在调用方手里——这就是把\"创建能力\"延迟交付的好处。\u003C\u002Fp>\n\u003Cp>现在想新增三角形？新建一个 Triangle 类、一个 TriangleFactory 类，就能接入使用了，\u003Cstrong>已有代码一行不动\u003C\u002Fstrong>，开闭原则保住了。代价也摆在那里：三种产品配了三个工厂类，类数量直接翻倍。\u003C\u002Fp>\n\u003Ch3>3. 抽象工厂\u003C\u002Fh3>\n\u003Cp>前两种工厂都只管一条产品线，但现实需求里经常是\u003Cstrong>一族产品\u003C\u002Fstrong>搭配出现：画图程序里除了图形还有颜色，图形和颜色就是两条产品线。抽象工厂模式让\"工厂\"本身也变成了被创建的对象——由一个超级工厂（工厂的工厂）来生产具体的工厂。\u003C\u002Fp>\n\u003Cp>先加一条颜色产品线：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public interface Color {\n    void fill();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-java\">public class Red implements Color {\n\n    public Red() {\n        System.out.println(\"创建了 Red 对象\");\n    }\n\n    @Override\n    public void fill() {\n        System.out.println(\"填充了红色\");\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Green、Blue 同理。然后是抽象工厂接口，注意它同时声明了两条产品线的创建能力：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public interface AbstractFactory {\n    Shape getShape(String shapeType);\n\n    Color getColor(String colorType);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>具体工厂各自负责一条产品线，造不了的产品类型返回 null：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class ShapeFactory implements AbstractFactory {\n\n    @Override\n    public Shape getShape(String shapeType) {\n        if (shapeType == null) {\n            return null;\n        }\n        if (shapeType.equalsIgnoreCase(\"CIRCLE\")) {\n            return new Circle();\n        } else if (shapeType.equalsIgnoreCase(\"RECTANGLE\")) {\n            return new Rectangle();\n        } else if (shapeType.equalsIgnoreCase(\"SQUARE\")) {\n            return new Square();\n        }\n        return null;\n    }\n\n    @Override\n    public Color getColor(String colorType) {\n        return null;\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>ColorFactory 则相反：getColor() 里做 RED\u002FGREEN\u002FBLUE 的判断，getShape() 返回 null。最后是\"工厂的工厂\"：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">public class FactoryProducer {\n\n    public static AbstractFactory getFactory(String factoryType) {\n        if (factoryType == null) {\n            return null;\n        }\n        if (factoryType.equalsIgnoreCase(\"SHAPE\")) {\n            return new ShapeFactory();\n        } else if (factoryType.equalsIgnoreCase(\"COLOR\")) {\n            return new ColorFactory();\n        }\n        return null;\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>测试代码：\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-java\">private static void abstractFactory() {\n    AbstractFactory shapeFactory = FactoryProducer.getFactory(\"SHAPE\");\n    Shape circle = shapeFactory.getShape(\"CIRCLE\");\n    Shape square = shapeFactory.getShape(\"SQUARE\");\n    circle.draw();\n    square.draw();\n    AbstractFactory colorFactory = FactoryProducer.getFactory(\"COLOR\");\n    Color red = colorFactory.getColor(\"RED\");\n    Color green = colorFactory.getColor(\"GREEN\");\n    red.fill();\n    green.fill();\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>创建了 Circle 对象\n创建了 Square 对象\n画了一个圆形\n画了一个正方形\n创建了 Red 对象\n创建了 Green 对象\n填充了红色\n填充了绿色\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>注意 ColorFactory 里 getShape() 返回的是 null——抽象工厂的强项是\u003Cstrong>同一条产品线内的横向扩展\u003C\u002Fstrong>：新增一种颜色，只需要改 ColorFactory 一处；但要是想新增一条全新的产品线（比如\"文字\"），AbstractFactory 接口一改，ShapeFactory、ColorFactory 所有具体工厂全部得跟着改。一句话总结：\u003Cstrong>横向（同族加产品）容易，纵向（加产品线）疼\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch2>五、三种工厂怎么选\u003C\u002Fh2>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>\u003C\u002Fth>\n   \u003Cth>简单工厂\u003C\u002Fth>\n   \u003Cth>工厂方法\u003C\u002Fth>\n   \u003Cth>抽象工厂\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>是否属于 GoF 23 种\u003C\u002Ftd>\n   \u003Ctd>否\u003C\u002Ftd>\n   \u003Ctd>是\u003C\u002Ftd>\n   \u003Ctd>是\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>工厂与产品的关系\u003C\u002Ftd>\n   \u003Ctd>一个工厂造所有产品\u003C\u002Ftd>\n   \u003Ctd>一个工厂造一种产品\u003C\u002Ftd>\n   \u003Ctd>一个工厂造一族产品\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>新增同类产品\u003C\u002Ftd>\n   \u003Ctd>修改工厂代码 ✗\u003C\u002Ftd>\n   \u003Ctd>新增类即可 ✓\u003C\u002Ftd>\n   \u003Ctd>同族内新增类即可 ✓\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>新增产品线\u003C\u002Ftd>\n   \u003Ctd>修改工厂代码 ✗\u003C\u002Ftd>\n   \u003Ctd>新增一套工厂接口\u003C\u002Ftd>\n   \u003Ctd>修改抽象工厂接口 ✗\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>类的数量\u003C\u002Ftd>\n   \u003Ctd>少\u003C\u002Ftd>\n   \u003Ctd>多\u003C\u002Ftd>\n   \u003Ctd>多\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>实际开发里的选型建议很朴素：\u003Cstrong>产品少且稳定，用简单工厂，图个轻量；产品会持续增加，用工厂方法，守住开闭原则；产品天然成族、需要保证同一族产品搭配使用时，再上抽象工厂\u003C\u002Fstrong>。绝大多数业务代码，一个简单工厂（或者简单工厂加策略模式）就够用了，别为了模式而模式。\u003C\u002Fp>\n\u003Ch2>写在最后\u003C\u002Fh2>\n\u003Cp>回顾一下这一篇：工厂模式把\"造对象\"从调用方手里收走，调用方从此只关心\"要什么\"。简单工厂一把梭但违背开闭原则；工厂方法一个产品一个工厂，类多了但扩展稳了；抽象工厂管住整个产品族，横向扩展容易、纵向扩展头疼。三种形态没有高下之分，只有取舍之别——而这，正是所有设计模式的共同底色。\u003C\u002Fp>\n\u003Cp>示例代码都放在 design-pattern 项目里，按模式分了包（singleton、factory.simple、factory.method、factory.abstractfactory），和上一篇的单例代码放在同一个项目下，方便对照练习。\u003C\u002Fp>\n\u003Cp>下一篇我们继续创建型模式，聊聊另一种\"造对象\"的思路——\u003Cstrong>建造者模式（Builder）\u003C\u002Fstrong>：当一个对象的构造参数多到 \u003Ccode>new Pizza(10, true, false, null, \"BIG\")\u003C\u002Fcode> 这种程度时，该怎么优雅地把它造出来。\u003C\u002Fp>",[],"index,follow",null,{"code":4,"message":5,"data":44},[45,54,63],{"id":46,"slug":47,"title":48,"summary":49,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":50,"likeCount":4,"wordCount":51,"readingMinutes":52,"publishedAt":53},"2113930718034792450","design-pattern-02","设计模式修炼之路 ② 单例模式——一个实例，六种写法","一个类在整个应用里只允许存在一个实例——听着是一句话需求，做起来是一路打怪：懒汉式、饿汉式、双重校验锁、静态内部类、枚举，六种写法沿着\"线程安全\"这条主线层层演进。",1708,5238,14,"2023-09-24T10:50:03",{"id":55,"slug":56,"title":57,"summary":58,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":59,"likeCount":4,"wordCount":60,"readingMinutes":61,"publishedAt":62},"2113931192066641922","design-pattern-04","设计模式修炼之路 ④ 建造者模式——告别重叠构造器","重叠构造器的坑、经典四角色建造者、Effective Java 式链式 Builder",3577,7441,19,"2023-11-06T18:16:44",{"id":64,"slug":65,"title":66,"summary":67,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":68,"likeCount":4,"wordCount":69,"readingMinutes":70,"publishedAt":71},"2113907939247919106","design-pattern-01","设计模式修炼之路 ① 初识设计模式——先把地图摊开","这是系列的第一篇，没有一行代码：设计模式是什么、模式分哪四大家族、六大原则长什么样。先把地图摊开，路从下一篇开始走。",3268,1903,5,"2023-09-10T12:19:35",{"code":4,"message":5,"data":73},{"prev":74,"next":75},{"id":46,"slug":47,"title":48,"summary":49,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":50,"likeCount":4,"wordCount":51,"readingMinutes":52,"publishedAt":53},{"id":55,"slug":56,"title":57,"summary":58,"coverUrl":9,"categoryId":32,"status":33,"topFlag":4,"viewCount":59,"likeCount":4,"wordCount":60,"readingMinutes":61,"publishedAt":62},{"code":4,"message":5,"data":77},[78,84,89,95],{"id":79,"name":80,"slug":81,"description":82,"sortOrder":4,"articleCount":83},"1946000000000000001","antlr4","tech","编程与技术笔记","13",{"id":32,"name":85,"slug":86,"description":87,"sortOrder":4,"articleCount":88},"设计模式","design-pattern","设计模式系列","34",{"id":90,"name":91,"slug":92,"description":93,"sortOrder":33,"articleCount":94},"1946000000000000002","随笔","essay","生活与思考","0",{"id":96,"name":97,"slug":98,"description":99,"sortOrder":100,"articleCount":101},"2105200093764698113","工具","tools","好用的工具推荐",2,"1",{"html":103,"toc":104},"\u003Cp>上一篇《设计模式修炼之路-单例模式》里，我们折腾了六种写法，本质上都是在解决一个问题：\u003Cstrong>一个类在整个应用里只允许存在一个实例\u003C\u002Fstrong>。单例模式管的是\"有几个\"，而这一篇要聊的工厂模式，管的是另一个同样高频的问题：\u003Cstrong>实例从哪来\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cp>\u003Ccode>new Circle()\u003C\u002Fcode> 谁都会写，但当 \u003Ccode>new\u003C\u002Fcode> 这个关键字散落在代码的各个角落时，麻烦就来了：一旦 Circle 的构造过程变了（比如要初始化依赖、要读配置、要打日志），所有 \u003Ccode>new Circle()\u003C\u002Fcode> 的地方都得跟着改。工厂模式的思路很简单：把\"造对象\"这件事收拢到一个专门的地方去，\u003Cstrong>调用方只管\"要\"，不管\"造\"\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch2 id=\"toc-1\">一、什么是工厂模式？\u003C\u002Fh2>\n\u003Cp>工厂模式（Factory Pattern）是 Java 中最常用的设计模式之一，属于\u003Cstrong>创建型模式\u003C\u002Fstrong>。它的核心思想用一句话就能说清楚：\u003C\u002Fp>\n\u003Cblockquote>\n \u003Cp>把对象的创建过程封装到一个\"工厂\"类中，调用方只需要告诉工厂\"我要什么\"，完全不关心对象是怎么被造出来的。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>拿去餐厅吃饭类比再合适不过：你坐下来跟服务员说\"来一份宫保鸡丁\"，十分钟后菜就端上来了。你不需要知道厨师是谁、厨房在哪、鸡丁是先焯水还是先过油——\u003Cstrong>厨房就是工厂，你就是调用方，菜单就是你们之间的接口\u003C\u002Fstrong>。哪天厨师换了做法，甚至整个后厨翻新，只要端上来的还是那盘宫保鸡丁，你完全无感。\u003C\u002Fp>\n\u003Cp>平时我们嘴上说的\"工厂模式\"，其实是三兄弟的统称：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>简单工厂（Simple Factory）\u003C\u002Fstrong>：严格来说它并不属于 GoF 的 23 种设计模式，但它是理解工厂模式的敲门砖——一个工厂包揽所有产品的创建，要什么传什么参数。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>工厂方法（Factory Method）\u003C\u002Fstrong>：GoF 23 种之一。一个工厂只生产一种产品，想新增产品？再开一家工厂。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>抽象工厂（Abstract Factory）\u003C\u002Fstrong>：GoF 23 种之一。一个工厂生产一整个\"产品族\"（一组相关的产品），连工厂本身都是被\"生产\"出来的。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>三兄弟是逐步演进的关系：简单工厂图省事，工厂方法补上开闭原则的漏洞，抽象工厂专门对付产品族。它们的具体区别放在第四节按顺序实现，实现完自然就懂了。\u003C\u002Fp>\n\u003Ch2 id=\"toc-2\">二、工厂模式的优缺点\u003C\u002Fh2>\n\u003Ch3 id=\"toc-3\">优点\u003C\u002Fh3>\n\u003Col>\n \u003Cli>\u003Cstrong>调用方和具体类解耦\u003C\u002Fstrong>。调用方依赖的是工厂和抽象接口，而不是一堆具体实现类，具体类内部怎么变都不影响调用方。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>创建逻辑收口\u003C\u002Fstrong>。对象的创建过程集中在一处，想在创建时加缓存、打日志、读配置，都只需要改工厂一个地方。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>符合开闭原则\u003C\u002Fstrong>（工厂方法模式）。新增一种产品时，只需要新增对应的产品类和工厂类，已有代码一行不动——上一篇提过的开闭原则，工厂方法是教科书级的示范。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3 id=\"toc-4\">缺点\u003C\u002Fh3>\n\u003Col>\n \u003Cli>\u003Cstrong>类的数量膨胀\u003C\u002Fstrong>。工厂方法模式下每种产品都要配一个工厂类，产品一多，类文件成倍增加，代码结构变复杂。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>简单工厂违背开闭原则\u003C\u002Fstrong>。新增产品就得改工厂类里的判断逻辑，\"对扩展开放，对修改关闭\"直接破防。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>抽象工厂纵向扩展难\u003C\u002Fstrong>。产品族里想新增一条全新的产品线，抽象工厂接口一改，所有具体工厂都得跟着改，牵一发动全身。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>可以看出，三种形态各有各的取舍，没有银弹——这也是设计模式一贯的画风：\u003Cstrong>用复杂度换取扩展性\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch2 id=\"toc-5\">三、工厂模式的应用场景\u003C\u002Fh2>\n\u003Cp>工厂模式太常见了，常见到你天天在用却未必意识到：\u003C\u002Fp>\n\u003Cul>\n \u003Cli>\u003Cstrong>JDK 里\u003C\u002Fstrong>：\u003Ccode>Calendar.getInstance()\u003C\u002Fcode>——你拿到的是 Calendar 的子类实例（比如 GregorianCalendar），但从来没 \u003Ccode>new\u003C\u002Fcode> 过它；\u003Ccode>Integer.valueOf()\u003C\u002Fcode> 也是类似的思路，还顺带做了缓存。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>Spring 里\u003C\u002Fstrong>：\u003Ccode>BeanFactory\u003C\u002Fcode>——整个 IoC 容器就是一个超级工厂，\u003Ccode>getBean()\u003C\u002Fcode> 就是工厂方法的放大版，你所有的 Bean 都是从这个工厂里\"造\"出来的。\u003C\u002Fli>\n \u003Cli>\u003Cstrong>JDBC 里\u003C\u002Fstrong>：\u003Ccode>DriverManager.getConnection()\u003C\u002Fcode>——数据库连接的创建细节被完全屏蔽，换数据库只是换了个\"供应商\"。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>什么时候该用\u003C\u002Fstrong>：当对象的创建过程比较复杂（依赖多、要初始化、要读配置），或者调用方只关心\"能用\"而不关心\"怎么造\"，或者同一族产品可能频繁增删时，就该考虑把创建逻辑收进工厂了。反过来，如果就是简单 \u003Ccode>new\u003C\u002Fcode> 一下的事，硬套工厂模式纯属过度设计。\u003C\u002Fp>\n\u003Ch2 id=\"toc-6\">四、工厂模式的实现方式\u003C\u002Fh2>\n\u003Cp>老规矩，光说不练假把式。本篇的示例代码按模式分包放在 design-pattern 项目里，目录结构如下：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>design-pattern\u002Fsrc\u002Fmain\u002Fjava\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── singleton                     \u002F\u002F 上一篇单例模式的六种实现\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>├── factory                       \u002F\u002F 工厂模式的公共产品\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── Shape.java                \u002F\u002F 图形产品接口\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── Circle.java \u002F Rectangle.java \u002F Square.java\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── simple                    \u002F\u002F 简单工厂\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   ├── method                    \u002F\u002F 工厂方法\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>│   └── abstractfactory           \u002F\u002F 抽象工厂\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>└── Test.java                     \u002F\u002F 测试入口\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>需求场景很简单：画图形。三种图形都实现 Shape 接口：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> interface\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Shape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Circle\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> implements\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Shape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Circle\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"创建了 Circle 对象\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"画了一个圆形\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Rectangle 和 Square 与之类似，不再重复贴。构造函数里特意加了一句打印，是为了从运行结果里看清对象到底是什么时候被创建的。\u003C\u002Fp>\n\u003Ch3 id=\"toc-7\">1. 简单工厂\u003C\u002Fh3>\n\u003Cp>简单工厂的思路最直白：开一家工厂，客户报什么名字，工厂就造什么。\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ShapeFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Shape \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">shapeType\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"CIRCLE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Circle\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"RECTANGLE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Rectangle\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"SQUARE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Square\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        return\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>测试代码（因为项目里有三个同名的 ShapeFactory，这里用全限定名调用，实际写代码时 import 对应包即可）：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">private\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> simpleFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Shape circle \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> factory.simple.ShapeFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"CIRCLE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Shape rectangle \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> factory.simple.ShapeFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"RECTANGLE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Shape square \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> factory.simple.ShapeFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"SQUARE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    circle.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    rectangle.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    square.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>创建了 Circle 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>创建了 Rectangle 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>创建了 Square 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个圆形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个长方形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个正方形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>调用方拿到的都是 Shape 类型，全程没出现一个 \u003Ccode>new Circle()\u003C\u002Fcode>，创建逻辑确实被收口了。但它的问题也很明显：\u003Cstrong>想新增一种\"三角形\"，就得回来改 getShape() 的 if-else\u003C\u002Fstrong>，修改了已经存在的类，开闭原则就此失守——这也是它没能进 GoF 23 种名单的原因。\u003C\u002Fp>\n\u003Cp>顺带一提，简单工厂还有一种进阶写法：用反射 \u003Ccode>Class.forName(shapeType).getDeclaredConstructor().newInstance()\u003C\u002Fcode> 把 if-else 干掉。能跑，但失去了编译期类型检查，参数传错要拖到运行时才炸，实际项目里并不推荐。\u003C\u002Fp>\n\u003Ch3 id=\"toc-8\">2. 工厂方法\u003C\u002Fh3>\n\u003Cp>简单工厂的病根在于\"一个工厂包办所有产品\"，工厂方法模式的药方就是：\u003Cstrong>把工厂也抽象出来，一个工厂只负责一种产品\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> interface\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ShapeFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Shape \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> CircleFactory\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> implements\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ShapeFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Shape \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Circle\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>RectangleFactory、SquareFactory 同理。测试代码：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">private\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> factoryMethod\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    ShapeFactory circleFactory \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> CircleFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    ShapeFactory rectangleFactory \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> RectangleFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    ShapeFactory squareFactory \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> SquareFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    circleFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">().\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    rectangleFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">().\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    squareFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">().\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>创建了 Circle 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个圆形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>创建了 Rectangle 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个长方形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>创建了 Square 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个正方形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>对比简单工厂的运行结果会发现一个有趣的细节：这次\"创建\"和\"画图\"是交替出现的，而简单工厂是先一口气全创建好再统一画。倒不是两种模式本身有这个区别，而是工厂方法交给调用方的是\u003Cstrong>工厂对象\u003C\u002Fstrong>，产品什么时候造、造几个，主动权完全在调用方手里——这就是把\"创建能力\"延迟交付的好处。\u003C\u002Fp>\n\u003Cp>现在想新增三角形？新建一个 Triangle 类、一个 TriangleFactory 类，就能接入使用了，\u003Cstrong>已有代码一行不动\u003C\u002Fstrong>，开闭原则保住了。代价也摆在那里：三种产品配了三个工厂类，类数量直接翻倍。\u003C\u002Fp>\n\u003Ch3 id=\"toc-9\">3. 抽象工厂\u003C\u002Fh3>\n\u003Cp>前两种工厂都只管一条产品线，但现实需求里经常是\u003Cstrong>一族产品\u003C\u002Fstrong>搭配出现：画图程序里除了图形还有颜色，图形和颜色就是两条产品线。抽象工厂模式让\"工厂\"本身也变成了被创建的对象——由一个超级工厂（工厂的工厂）来生产具体的工厂。\u003C\u002Fp>\n\u003Cp>先加一条颜色产品线：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> interface\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Color\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> fill\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Red\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> implements\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Color\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Red\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"创建了 Red 对象\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> fill\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        System.out.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">println\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"填充了红色\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Green、Blue 同理。然后是抽象工厂接口，注意它同时声明了两条产品线的创建能力：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> interface\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> AbstractFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Shape \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">shapeType\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Color \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getColor\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">colorType\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>具体工厂各自负责一条产品线，造不了的产品类型返回 null：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ShapeFactory\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> implements\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> AbstractFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Shape \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">shapeType\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"CIRCLE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Circle\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"RECTANGLE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Rectangle\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (shapeType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"SQUARE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> Square\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        return\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    @\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">Override\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> Color \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getColor\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">colorType\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        return\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>ColorFactory 则相反：getColor() 里做 RED\u002FGREEN\u002FBLUE 的判断，getShape() 返回 null。最后是\"工厂的工厂\"：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> class\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> FactoryProducer\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    public\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> AbstractFactory \u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(String \u003C\u002Fspan>\u003Cspan style=\"color:#E36209;--shiki-dark:#FFAB70\">factoryType\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (factoryType \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">==\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (factoryType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"SHAPE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ShapeFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        } \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">else\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> if\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (factoryType.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">equalsIgnoreCase\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"COLOR\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)) {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">            return\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> new\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> ColorFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        return\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> null\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>测试代码：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">private\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> static\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> void\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> abstractFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">() {\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    AbstractFactory shapeFactory \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> FactoryProducer.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"SHAPE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Shape circle \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> shapeFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"CIRCLE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Shape square \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> shapeFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getShape\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"SQUARE\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    circle.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    square.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">draw\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    AbstractFactory colorFactory \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> FactoryProducer.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getFactory\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"COLOR\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Color red \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> colorFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getColor\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"RED\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    Color green \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> colorFactory.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">getColor\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"GREEN\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    red.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">fill\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    green.\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">fill\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">();\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>运行结果：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>创建了 Circle 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>创建了 Square 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个圆形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>画了一个正方形\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>创建了 Red 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>创建了 Green 对象\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>填充了红色\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>填充了绿色\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>注意 ColorFactory 里 getShape() 返回的是 null——抽象工厂的强项是\u003Cstrong>同一条产品线内的横向扩展\u003C\u002Fstrong>：新增一种颜色，只需要改 ColorFactory 一处；但要是想新增一条全新的产品线（比如\"文字\"），AbstractFactory 接口一改，ShapeFactory、ColorFactory 所有具体工厂全部得跟着改。一句话总结：\u003Cstrong>横向（同族加产品）容易，纵向（加产品线）疼\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003Ch2 id=\"toc-10\">五、三种工厂怎么选\u003C\u002Fh2>\n\u003Ctable>\n \u003Cthead>\n  \u003Ctr>\n   \u003Cth>\u003C\u002Fth>\n   \u003Cth>简单工厂\u003C\u002Fth>\n   \u003Cth>工厂方法\u003C\u002Fth>\n   \u003Cth>抽象工厂\u003C\u002Fth>\n  \u003C\u002Ftr>\n \u003C\u002Fthead>\n \u003Ctbody>\n  \u003Ctr>\n   \u003Ctd>是否属于 GoF 23 种\u003C\u002Ftd>\n   \u003Ctd>否\u003C\u002Ftd>\n   \u003Ctd>是\u003C\u002Ftd>\n   \u003Ctd>是\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>工厂与产品的关系\u003C\u002Ftd>\n   \u003Ctd>一个工厂造所有产品\u003C\u002Ftd>\n   \u003Ctd>一个工厂造一种产品\u003C\u002Ftd>\n   \u003Ctd>一个工厂造一族产品\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>新增同类产品\u003C\u002Ftd>\n   \u003Ctd>修改工厂代码 ✗\u003C\u002Ftd>\n   \u003Ctd>新增类即可 ✓\u003C\u002Ftd>\n   \u003Ctd>同族内新增类即可 ✓\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>新增产品线\u003C\u002Ftd>\n   \u003Ctd>修改工厂代码 ✗\u003C\u002Ftd>\n   \u003Ctd>新增一套工厂接口\u003C\u002Ftd>\n   \u003Ctd>修改抽象工厂接口 ✗\u003C\u002Ftd>\n  \u003C\u002Ftr>\n  \u003Ctr>\n   \u003Ctd>类的数量\u003C\u002Ftd>\n   \u003Ctd>少\u003C\u002Ftd>\n   \u003Ctd>多\u003C\u002Ftd>\n   \u003Ctd>多\u003C\u002Ftd>\n  \u003C\u002Ftr>\n \u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>实际开发里的选型建议很朴素：\u003Cstrong>产品少且稳定，用简单工厂，图个轻量；产品会持续增加，用工厂方法，守住开闭原则；产品天然成族、需要保证同一族产品搭配使用时，再上抽象工厂\u003C\u002Fstrong>。绝大多数业务代码，一个简单工厂（或者简单工厂加策略模式）就够用了，别为了模式而模式。\u003C\u002Fp>\n\u003Ch2 id=\"toc-11\">写在最后\u003C\u002Fh2>\n\u003Cp>回顾一下这一篇：工厂模式把\"造对象\"从调用方手里收走，调用方从此只关心\"要什么\"。简单工厂一把梭但违背开闭原则；工厂方法一个产品一个工厂，类多了但扩展稳了；抽象工厂管住整个产品族，横向扩展容易、纵向扩展头疼。三种形态没有高下之分，只有取舍之别——而这，正是所有设计模式的共同底色。\u003C\u002Fp>\n\u003Cp>示例代码都放在 design-pattern 项目里，按模式分了包（singleton、factory.simple、factory.method、factory.abstractfactory），和上一篇的单例代码放在同一个项目下，方便对照练习。\u003C\u002Fp>\n\u003Cp>下一篇我们继续创建型模式，聊聊另一种\"造对象\"的思路——\u003Cstrong>建造者模式（Builder）\u003C\u002Fstrong>：当一个对象的构造参数多到 \u003Ccode>new Pizza(10, true, false, null, \"BIG\")\u003C\u002Fcode> 这种程度时，该怎么优雅地把它造出来。\u003C\u002Fp>",[105,108,111,115,118,121,124,127,130,133,136],{"id":106,"text":107,"level":100},"toc-1","一、什么是工厂模式？",{"id":109,"text":110,"level":100},"toc-2","二、工厂模式的优缺点",{"id":112,"text":113,"level":114},"toc-3","优点",3,{"id":116,"text":117,"level":114},"toc-4","缺点",{"id":119,"text":120,"level":100},"toc-5","三、工厂模式的应用场景",{"id":122,"text":123,"level":100},"toc-6","四、工厂模式的实现方式",{"id":125,"text":126,"level":114},"toc-7","1. 简单工厂",{"id":128,"text":129,"level":114},"toc-8","2. 工厂方法",{"id":131,"text":132,"level":114},"toc-9","3. 抽象工厂",{"id":134,"text":135,"level":100},"toc-10","五、三种工厂怎么选",{"id":137,"text":138,"level":100},"toc-11","写在最后"]