设计模式--工厂模式
1、 什么是工厂模式
工厂模式的核心目的是将对象的创建与使用分离,它提供了一种创建对象的接口,但让子类决定实例化哪一个类。工厂模式让类的实例化延迟到子类进行。
工厂模式主要分为三种类型,解决不同层次的创建问题:
简单工厂模式 (Simple Factory Pattern) / 静态工厂方法模式 (Static Factory Method Pattern)
工厂方法模式 (Factory Method Pattern)
抽象工厂模式 (Abstract Factory Pattern)
2. 简单工厂模式
核心思想: 定义一个工厂类,它根据传入的参数不同,创建并返回不同的具体产品对象。客户端不需要知道具体产品的类名,只需要知道传递给工厂的参数。
结构:
产品接口 (Product Interface): 定义所有具体产品需要实现的公共方法。
具体产品类 (Concrete Products): 实现产品接口的具体类。
工厂类 (Simple Factory): 包含一个静态方法(通常是
createProduct
或类似名字),该方法接收一个参数(如类型标识符),根据这个参数创建并返回对应的具体产品对象。Java 代码示例:
// 1. 产品接口 public interface Phone {void make(); }// 2. 具体产品类 public class IPhone implements Phone {@Overridepublic void make() {System.out.println("Making an iPhone...");} }public class AndroidPhone implements Phone {@Overridepublic void make() {System.out.println("Making an Android phone...");} }// 3. 简单工厂 public class SimplePhoneFactory {// 静态工厂方法,根据类型创建产品public static Phone createPhone(String type) {//equalsIgnoreCase:在比较字符串时不区分大小写if ("iphone".equalsIgnoreCase(type)) {return new IPhone();} else if ("android".equalsIgnoreCase(type)) {return new AndroidPhone();} else {throw new IllegalArgumentException("Unknown phone type: " + type);}} }// 4. 客户端使用 public class Client {public static void main(String[] args) {// 客户端只需要告诉工厂需要什么类型的产品,不需要知道具体类Phone phone1 = SimplePhoneFactory.createPhone("iphone");phone1.make(); // 输出: Making an iPhone...Phone phone2 = SimplePhoneFactory.createPhone("android");phone2.make(); // 输出: Making an Android phone...} }
优点:
客户端与具体产品类解耦。客户端只需要知道产品接口和工厂类。
将对象的创建逻辑集中在一个地方,便于管理和维护(比如修改创建逻辑、添加日志等)。
缺点:
违反开闭原则 (Open-Closed Principle): 当需要添加新的具体产品时(比如
HuaweiPhone
),必须修改工厂类(SimplePhoneFactory
)中的createPhone
方法,添加新的if-else
分支。工厂类职责过重,如果产品种类非常多,工厂方法会变得非常臃肿(一个巨大的
switch-case
或if-else
块)。静态方法使得工厂类无法基于继承被扩展。
3. 工厂方法模式
核心思想: 定义一个用于创建对象的接口(工厂接口),但让子类决定实例化哪一个具体产品类。工厂方法让类的实例化延迟到其子类。
结构:
产品接口 (Product Interface): 同上。
具体产品类 (Concrete Products): 同上。
工厂接口/抽象工厂类 (Creator): 声明工厂方法 (
createProduct()
),该方法返回一个产品对象。这个接口/抽象类可能还包含一些依赖于产品对象的业务逻辑。具体工厂类 (Concrete Creators): 实现或重写工厂接口/抽象工厂类中声明的工厂方法,负责创建特定的具体产品对象。
Java 代码示例:
// 1. 产品接口 public interface Phone {void make(); }// 2. 具体产品类 public class IPhone implements Phone {@Overridepublic void make() {System.out.println("Making an iPhone...");} }public class AndroidPhone implements Phone {@Overridepublic void make() {System.out.println("Making an Android phone...");} }// 3. 工厂接口 (也可以定义成抽象类) public interface PhoneFactory {Phone createPhone(); // 工厂方法 }// 4. 具体工厂类 (每个具体产品对应一个具体工厂) public class IPhoneFactory implements PhoneFactory {@Overridepublic Phone createPhone() {return new IPhone(); // 负责创建iPhone} }public class AndroidPhoneFactory implements PhoneFactory {@Overridepublic Phone createPhone() {return new AndroidPhone(); // 负责创建AndroidPhone} }// 5. 客户端使用 public class Client {public static void main(String[] args) {// 客户端选择使用哪个具体工厂PhoneFactory iphoneFactory = new IPhoneFactory();Phone phone1 = iphoneFactory.createPhone(); // 通过工厂方法创建产品phone1.make(); // 输出: Making an iPhone...PhoneFactory androidFactory = new AndroidPhoneFactory();Phone phone2 = androidFactory.createPhone();phone2.make(); // 输出: Making an Android phone...} }
优点:
完全符合开闭原则: 当需要添加新的具体产品(如
HuaweiPhone
)时,只需要添加一个新的具体工厂类(HuaweiPhoneFactory
)实现工厂接口即可。不需要修改现有的工厂接口和客户端代码(假设客户端通过配置或依赖注入等方式获得工厂)。客户端代码只依赖于产品接口和工厂接口,与具体产品类和具体工厂类解耦。
单一职责原则:每个具体工厂只负责创建一种产品,职责清晰。
缺点:
类的数量增多。每增加一个产品,就需要增加一个具体产品类和一个具体工厂类,系统复杂度增加。
增加了系统的抽象性和理解难度。
4.抽象工厂模式
核心思想: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。它围绕一个超级工厂创建其他工厂。抽象工厂模式是工厂方法模式的扩展,用于创建产品族(多个相关的产品)。
结构:
抽象产品接口 (Abstract Product Interfaces): 定义一类产品的接口。通常有多个抽象产品接口(如
Phone
,Tablet
,Watch
)。具体产品类 (Concrete Products): 实现特定的抽象产品接口,属于同一个产品族(如
IPhone
,IPad
,AppleWatch
都属于 Apple 产品族;GalaxyPhone
,GalaxyTab
,GalaxyWatch
都属于 Samsung 产品族)。抽象工厂接口/类 (Abstract Factory): 声明一组用于创建不同抽象产品的方法(如
createPhone()
,createTablet()
,createWatch()
)。每个方法返回一个抽象产品。具体工厂类 (Concrete Factories): 实现抽象工厂接口/类。每个具体工厂负责创建属于特定产品族的所有具体产品(如
AppleFactory
创建Phone
,IPad
,AppleWatch
;SamsungFactory
创建GalaxyPhone
,GalaxyTab
,GalaxyWatch
)。Java 代码示例:
// 1. 产品接口 public interface Phone {void make(); }// 2. 具体产品类 public class IPhone implements Phone {@Overridepublic void make() {System.out.println("Making an iPhone...");} }public class AndroidPhone implements Phone {@Overridepublic void make() {System.out.println("Making an Android phone...");} }// 3. 工厂接口 (也可以定义成抽象类) public interface PhoneFactory {Phone createPhone(); // 工厂方法 }// 4. 具体工厂类 (每个具体产品对应一个具体工厂) public class IPhoneFactory implements PhoneFactory {@Overridepublic Phone createPhone() {return new IPhone(); // 负责创建iPhone} }public class AndroidPhoneFactory implements PhoneFactory {@Overridepublic Phone createPhone() {return new AndroidPhone(); // 负责创建AndroidPhone} }// 5. 客户端使用 public class Client {public static void main(String[] args) {// 客户端选择使用哪个具体工厂PhoneFactory iphoneFactory = new IPhoneFactory();Phone phone1 = iphoneFactory.createPhone(); // 通过工厂方法创建产品phone1.make(); // 输出: Making an iPhone...PhoneFactory androidFactory = new AndroidPhoneFactory();Phone phone2 = androidFactory.createPhone();phone2.make(); // 输出: Making an Android phone...} }
优点:
保证产品兼容性: 确保客户端创建的所有产品都来自同一个产品族(如都是 Apple 风格或都是 Samsung 风格),避免不兼容产品的组合。
符合开闭原则 (针对产品族): 添加一个新的产品族(如 Huawei 产品族)非常容易,只需要添加一个新的具体工厂(
HuaweiFactory
)和该族下的所有具体产品类(HuaweiPhone
,HuaweiTablet
),不需要修改抽象工厂和已有的具体工厂。单一职责/开闭原则 (针对产品等级): 将创建不同产品族的逻辑隔离在不同的具体工厂中。
缺点:
难以支持新种类的产品: 如果需要在抽象工厂接口中添加一个新的产品类型(如
createWatch()
),那么所有的具体工厂(AppleFactory
,SamsungFactory
,HuaweiFactory
)都需要被修改来实现这个新方法,这违反了开闭原则(针对产品等级结构)。类的数量剧增: 产品等级(产品种类,如 Phone, Tablet)和产品族(品牌,如 Apple, Samsung)越多,需要的具体工厂和具体产品类就越多,系统变得非常庞大和复杂。
增加了系统的抽象性和理解难度。
5.总结与对比
特性 简单工厂模式 工厂方法模式 抽象工厂模式 目的 封装对象创建,隐藏细节 让子类决定实例化哪个类 创建相关或依赖的产品族 关键参与者 工厂类(含静态方法) 工厂接口 + 具体工厂类 抽象工厂接口 + 具体工厂类 创建对象 单一类型的不同产品 单一类型的不同产品 多个相关类型 (产品族) 的不同产品 开闭原则 违反 (添加新产品需改工厂) 支持 (添加新产品只需加新工厂) 支持产品族扩展 (添加新族容易)
不支持产品等级扩展 (添加新种类难)复杂度 低 中 高 类数量 少 较多 (每产品一工厂) 非常多 (每族 * 每产品等级) 适用场景 产品种类少且稳定,不常扩展 产品种类可能扩展,客户端只需一种 需要创建多个相关对象,
且这些对象需要属于同一族或兼容6.如何选择?
简单工厂: 适用于对象创建逻辑简单,产品种类相对固定且不会频繁增加的情况。快速有效,但扩展性差。
工厂方法: 适用于只涉及创建单一类型产品,但未来可能需要扩展该类型下具体产品的情况。是解耦和扩展性的良好平衡。
抽象工厂: 适用于需要创建多个相互关联或依赖的对象(一个产品族),并且需要保证这些对象兼容的场景(如 GUI 库创建不同操作系统的窗口、按钮、文本框)。当产品族相对稳定,但具体产品可能变化或扩展时最合适。
理解工厂模式的这三种形态及其适用场景,对于设计灵活、可维护、可扩展的 Java 应用程序至关重要。它们都服务于“解耦创建者与具体类型”这一核心目标,只是在不同复杂度层面提供了解决方案。