——由子类决定实例化哪个类的对象创建模式)
CS-Notes 设计模式解析工厂方法Factory Method——由子类决定实例化哪个类的对象创建模式【免费下载链接】CS-Notes:books: 技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计项目地址: https://gitcode.com/GitHub_Trending/cs/CS-Notes工厂方法Factory Method是 CS-Notes 设计模式系列中创建型模式的核心知识点之一与本仓库 设计模式 - 简单工厂、设计模式 - 抽象工厂 文档相互呼应共同构成工厂家族的完整学习链路。本文以 原始文档 为骨架结合同一仓库中 设计模式全篇合集 对工厂方法章节的完整展开从模式意图、类图结构、Java 实现、与简单工厂/抽象工厂的边界辨析到 JDK 标准库中的真实应用逐层解析让读者既能在面试中讲清为什么工厂方法要把实例化推迟到子类也能在实际编码中判断何时该引入该模式。一、模式意图把实例化哪个类的决定权下沉给子类原始文档对工厂方法 Intent 的概括非常精炼定义了一个创建对象的接口但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。拆解这句话可以得到两个关键点定义了一个创建对象的接口——在抽象父类或接口中声明一个用于创建产品的方法即factoryMethod()它只约定返回一个 Product而不关心具体返回哪一个产品实现由子类决定要实例化哪个类——具体返回ConcreteProduct、ConcreteProduct1还是ConcreteProduct2由继承该父类的不同子类工厂各自覆盖实现来决定父类代码完全不感知具体产品类型。这就是实例化操作推迟到子类的含义对象创建时机的决策点从使用方/父类移到了子类继承体系上父类只在抽象层面描述我需要一个产品。public abstract class Factory { abstract public Product factoryMethod(); public void doSomething() { Product product factoryMethod(); // do something with the product } }二、演进动机为什么是由子类来创建对象原始文档在 Class Diagram 一节开宗明义地指出了它与简单工厂的本质差异在简单工厂中创建对象的是另一个类而在工厂方法中是由子类来创建对象。在简单工厂方案里所有实例化逻辑if-else选择具体产品被集中收敛进一个独立的SimpleFactory类客户端不再直接new具体产品而是把要哪种产品的参数交给工厂public class SimpleFactory { public Product createProduct(int type) { if (type 1) { return new ConcreteProduct1(); } else if (type 2) { return new ConcreteProduct2(); } return new ConcreteProduct(); } }简单工厂解决了客户类与具体子类实现解耦的问题但选择逻辑依然集中在工厂内部的一段分支代码上。每增加一种产品仍要修改SimpleFactory.createProduct()本身。工厂方法的思路则是把选择改成覆盖不再由某个旁路工厂类的if-else决定返回哪个产品而是每种具体产品都对应一个具体工厂子类父类只声明抽象的factoryMethod()由继承它的具体子类各自决定实例化哪个产品。这样决定要实例化哪个类的逻辑就被分散、固化到了每个子类中新增产品时通过新增一个具体工厂子类来扩展而无需改动既有工厂代码。下图是 简单工厂的类图 对应的结构Client依赖SimpleFactory由SimpleFactory统一创建ConcreteProduct系列与之形成对照的是工厂方法的类图见下节创建职责从另一个独立类转移到了工厂自身的子类。三、结构解析四个角色的职责划分原始文档给出的类图用到了Factory与ConcreteFactory两级继承结构图示文件保存在 notes/pics/f4d0afd0-8e78-4914-9e60-4366eaf065b5.png对照类图模式中通常包含以下角色角色对应类职责产品抽象Product定义产品的抽象类型是工厂方法返回值的静态类型具体产品ConcreteProduct/ConcreteProduct1/ConcreteProduct2实现Product的具体产品类抽象工厂CreatorFactory声明抽象的factoryMethod(): Product并定义使用产品的业务方法doSomething()具体工厂ConcreteCreatorConcreteFactory/ConcreteFactory1/ConcreteFactory2覆盖factoryMethod()返回各自对应的具体产品原始文档对类图的关键讲解是下图中Factory 有一个 doSomething() 方法这个方法需要用到一个产品对象这个产品对象由 factoryMethod() 方法创建。该方法是抽象的需要由子类去实现。这段描述包含了一个容易忽略的细节doSomething()是父类里已经写好的业务骨架而它所需的产品却要在运行时才能确定。父类的doSomething()在编译期只知道调用factoryMethod()会得到一个Product至于这个Product究竟是哪一种实现要到运行期由对象的真实类型哪个具体工厂子类来决定。细看可以发现doSomething()定义流程、把其中创建产品这一步骤延迟到子类实现的结构与本仓库 设计模式 - 模板方法 中CaffeineBeverage 定义算法框架、将部分步骤延迟到子类的思路一致——这也是工厂方法常被看作一种特殊的模板方法应用的原因。四、Java 实现逐块解读抽象父类 多个具体子类工厂原始文档共给出 4 段核心代码构成一套完整的工厂方法骨架。下面逐段解读并补充让整套代码可独立编译运行所必需的产品类定义与调用方示例。4.1 抽象工厂声明工厂方法 复用业务方法public abstract class Factory { abstract public Product factoryMethod(); public void doSomething() { Product product factoryMethod(); // do something with the product } }要点factoryMethod()被声明为抽象方法强制所有子类工厂给出我生产哪种产品的实现doSomething()是父类提供的可复用业务方法它只面向Product抽象编程通过调用factoryMethod()获得产品后继续处理由于doSomething()写在了父类中所有具体工厂子类都能继承同一套业务逻辑区别只在最终拿到的是哪种产品。4.2 具体工厂每种产品对应一个子类工厂public class ConcreteFactory extends Factory { public Product factoryMethod() { return new ConcreteProduct(); } }public class ConcreteFactory1 extends Factory { public Product factoryMethod() { return new ConcreteProduct1(); } }public class ConcreteFactory2 extends Factory { public Product factoryMethod() { return new ConcreteProduct2(); } }三个具体工厂分别继承Factory并覆盖factoryMethod()各自返回ConcreteProduct、ConcreteProduct1、ConcreteProduct2。调用方只要持有某个具体工厂的实例就能稳定地得到它对应的产品——实例化逻辑被封装在每个具体工厂内部与doSomething()的业务逻辑完全解耦。4.3 补齐产品定义形成可运行的完整示例工厂方法文档本身只关心工厂侧代码其引用的Product、ConcreteProduct等类型在本仓库 简单工厂文档 的示例中有明确定义可直接复用同一套产品骨架public interface Product { }public class ConcreteProduct implements Product { }public class ConcreteProduct1 implements Product { }public class ConcreteProduct2 implements Product { }在此基础上调用方Client只需要面向抽象工厂Factory编程选择用哪个具体工厂就会在doSomething()内部获得对应产品public class Client { public static void main(String[] args) { Factory factory new ConcreteFactory1(); factory.doSomething(); // 内部通过 factoryMethod() 得到 ConcreteProduct1 Factory factory2 new ConcreteFactory2(); factory2.doSomething(); // 内部通过 factoryMethod() 得到 ConcreteProduct2 } }这段调用方代码体现了两层解耦Client 不直接new ConcreteProduct1甚至不引用任何具体产品类名doSomething()的业务逻辑完全不依赖具体产品把产品创建这一步骤交给子类按需实现。五、与抽象工厂的边界单对象 vs 相关对象家族工厂方法与抽象工厂是工厂家族中最容易被混淆的一对。本仓库 抽象工厂文档 专门对此做了辨析原文要点如下抽象工厂模式创建的是对象家族也就是很多对象而不是一个对象并且这些对象是相关的也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象这和抽象工厂模式有很大不同。抽象工厂模式用到了工厂方法模式来创建单一对象AbstractFactory 中的 createProductA() 和 createProductB() 方法都是让子类来实现这两个方法单独来看就是在创建一个对象这符合工厂方法模式的定义。从高层次来看抽象工厂使用了组合即 Client 组合了 AbstractFactory而工厂方法模式使用了继承。可以归纳出三条清晰的边界对比维度工厂方法抽象工厂创建数量只创建一个产品对象创建一组相关的产品对象对象家族实现手段Client/父类面向Factory通过继承让子类覆盖工厂方法Client组合AbstractFactory一次调用多个创建方法得到整套对象族层次关系基础模式上层模式其内部每个createProductX()单独看就是一个工厂方法抽象工厂的类图可以直观看出一个具体工厂对应多个产品的差别图示文件notes/pics/e2190c36-8b27-4690-bde5-9911020a1294.png理解二者关系有助于确定选型当只需要一个方法产出一种对象用工厂方法即可当业务上必须成组地创建相互关联的多个对象如一套 UI 风格下的按钮输入框则应升级为抽象工厂且抽象工厂内部依然在用工厂方法组织单对象创建逻辑。六、工厂方法在 JDK 标准库中的应用原始文档在 JDK 一节给出了 7 个典型应用。这些 API 的共同特征是公开的创建入口往往返回抽象类型的实例而真正实例化哪个子类由内部依据时区、语言环境、参数等运行时信息决定调用方无从也不必感知JDK 类与方法工厂方法的体现java.util.Calendar#getInstance()Calendar是抽象类getInstance()依据当前时区与语言环境返回其某个具体子类实例调用方只面向Calendar编程java.util.ResourceBundle#getBundle(String)根据资源基名与语言环境定位并返回对应的资源包子类实例java.text.NumberFormat#getInstance()返回基于默认语言环境的具体数字格式器如DecimalFormat实例java.nio.charset.Charset#forName(String)按字符集名称返回对应的Charset实例java.net.URLStreamHandlerFactory#createURLStreamHandler(String)定义创建URLStreamHandler的工厂接口由不同协议的具体实现提供对应处理器java.util.EnumSet#of(E...)以静态工厂形式创建枚举集合并会按枚举规模的差异返回不同的内部实现子类javax.xml.bind.JAXBContext#createMarshaller()由上下文对象统一创建Marshaller实例屏蔽底层构造细节这些例子共同说明工厂方法在标准库中的典型落点当某个库希望把创建哪个实现类的决策保留给自己、只向外界暴露抽象类型时工厂方法是最自然的封装方式——这也正是阅读源码时识别该模式的信号。七、使用场景与权衡小结综合原始文档的意图描述与仓库中同系列文档的对照可以总结出以下判断框架适合使用工厂方法的情形父类中存在一段不依赖具体产品的公共业务逻辑对应doSomething()希望在逻辑内部获得产品对象但不希望父类耦合具体产品类产品的具体类型预期会持续扩展希望新增产品 新增一个具体产品类 新增一个具体工厂子类尽量不改动既有调用方与既有工厂希望将对象创建的细节new哪个类从使用方代码中剥离让 Client 只面向Product与Factory抽象编程。需要权衡的点每引入一种具体产品通常就要新增一个具体工厂类类的数量会随之增长简单场景下反而显得繁琐当需求演进为必须成组创建相互关联的多对象家族时单靠工厂方法已不足够应参考 抽象工厂 的组合式设计若产品类型不多、且创建逻辑变化频率低直接使用 简单工厂 的集中式createProduct(type)可能更轻量。八、仓库内延伸学习路径阅读 设计模式 - 工厂方法.md 原始笔记对照本节类图与代码在 设计模式全篇合集notes/设计模式.md 中查看工厂方法章节及前后文语境简单工厂紧随其后是工厂方法与抽象工厂通过 设计模式目录索引 定位创建型模式的完整目录顺次阅读 简单工厂 → 工厂方法 → 抽象工厂 形成工厂家族的递进认知与 生成器Builder、单例 等其他创建型模式对比理解何时按步骤构造对象、何时全局唯一、何时交由子类决定实例化类型的各自定位。【免费下载链接】CS-Notes:books: 技术面试必备基础知识、Leetcode、计算机操作系统、计算机网络、系统设计项目地址: https://gitcode.com/GitHub_Trending/cs/CS-Notes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考