定义
工厂模式(Factory Pattern)通常指的是工厂方法模式(Factory Method Pattern),它定义了一个创建对象的方法,由子类决定要实例化的类。工厂方法让类的实例化推迟到子类。
抽象工厂模式(Abstract Factory Pattern)提供了一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。抽象工厂允许客户端使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际生产的具体产品是什么。
应用场景
工厂方法适用于以下场景:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当类希望由其子类来指定创建对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪个帮助子类是代理者这一信息局部化的时候。
抽象工厂适用于以下场景:
- 当需要创建的对象是一系列相关或相互依赖的产品族时。
- 当一个系统要独立于它的产品的创建、组合和表示时。
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
示例与反例
工厂方法示例:
假设你有一个日志记录器的应用,你想要支持多种日志记录方式,例如文件日志记录和数据库日志记录。你可以创建一个日志记录器接口和实现该接口的多个具体日志记录器。然后定义一个工厂接口,让子类决定实例化哪一个日志记录器。
public interface Logger {
void log(String message);
}
public class FileLogger implements Logger {
public void log(String message) {
// 文件日志记录实现
}
}
public class DatabaseLogger implements Logger {
public void log(String message) {
// 数据库日志记录实现
}
}
public abstract class LoggerFactory {
abstract Logger createLogger();
}
public class FileLoggerFactory extends LoggerFactory {
Logger createLogger() {
return new FileLogger();
}
}
public class DatabaseLoggerFactory extends LoggerFactory {
Logger createLogger() {
return new DatabaseLogger();
}
}
抽象工厂示例:
如果你的应用需要在不同的操作系统下运行,并且每个操作系统都有一套不同的UI组件(如按钮、文本框等)。你可以为每个操作系统实现一套组件,并使用抽象工厂模式来创建和使用这些组件,而无需关心具体的实现细节。
// UI组件的抽象产品
public interface Button {
void paint();
}
public interface TextBox {
void paint();
}
// 抽象工厂
public interface GUIFactory {
Button createButton();
TextBox createTextBox();
}
// Windows具体产品
public class WinButton implements Button {
public void paint() {
// Windows风格按钮
}
}
public class WinTextBox implements TextBox {
public void paint() {
// Windows风格文本框
}
}
// MacOS具体产品
public class MacButton implements Button {
public void paint() {
// MacOS风格按钮
}
}
public class MacTextBox implements TextBox {
public void paint() {
// MacOS风格文本框
}
}
// Windows工厂
public class WinFactory implements GUIFactory {
public Button createButton() {
return new WinButton();
}
public TextBox createTextBox() {
return new WinTextBox();
}
}
// MacOS工厂
public class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton();
}
public TextBox createTextBox() {
return new MacTextBox();
}
}
反例:
对于简单的对象创建,没有必要使用工厂方法或抽象工厂模式,这会增加不必要的复杂性。如果对象可以直接通过构造函数创建,并且不需要额外的逻辑来选择具体的实现,那么就应该直接创建对象实例。
原则间的权衡与冲突
工厂方法和抽象工厂都支持开闭原则,使得系统易于扩展。同时,它们也倾向于遵循依赖倒置原则,因为客户端代码依赖于抽象而不是具体的实现。
然而,这两种模式都可能导致类的数量增加,从而增加系统的复杂性。这可能与简洁性(KISS)原则相冲突,该原则鼓励尽可能保持设计的简单性。
设计模式的局限性
工厂方法和抽象工厂模式的主要局限性是它们有时可能不必要地增加系统的复杂性,特别是当你的产品种类和变化不多时。另外,抽象工厂难以支持新种类产品的增加,因为它要求修改抽象工厂接口,这违反了开闭原则。
总结与建议
在选择是否使用工厂方法或抽象工厂模式时,应该考虑如下因素:
- 如果产品的创建逻辑较为复杂,或者需要根据环境不同而创建不同的对象,并且这种差异仅限于单一产品,那么工厂方法可能是一个好选择。
- 如果需要创建一系列相关或相互依赖的产品,而且你想隐藏这些产品的具体实现细节,那么抽象工厂模式可能更适合。
在实际应用中,应当根据产品的复杂性、变化的频率、系统设计的灵活性和可扩展性的需求来选择合适的模式。如果系统中产品的变化不频繁,那么可以考虑使用简单工厂或者静态工厂方法,以减少不必要的设计复杂度。