第2章:创建型模式(Creational Patterns)
一、导读
1.1 本章学习目标
第2章介绍 5 个创建型模式。创建型模式封装了对象创建的过程,将对象的创建与使用分离,使系统设计更加灵活。
本章的核心学习目标包括:
- 掌握 Factory Method:定义创建对象的接口,让子类决定实例化哪个类
- 理解 Abstract Factory:创建一系列相关对象
- 学会 Builder:分步骤构建复杂对象
- 掌握 Prototype:通过复制现有对象创建新对象
- 理解 Singleton:确保一个类只有一个实例
1.2 为什么本章如此重要
创建型模式是所有设计模式中最基础的一类。它们解决的核心问题是:如何灵活地创建对象。在很多系统中,对象的创建逻辑是复杂且多变的——如果将创建逻辑硬编码在使用处,系统就会变得僵化。创建型模式通过将创建逻辑封装起来,使系统更加灵活和可扩展。
1.3 核心问题引导
Factory Method 与 Abstract Factory 有什么区别?
什么时候应该使用 Builder 而不是构造函数?
Prototype 模式在什么场景下有用?
Singleton 模式有什么争议?为什么很多人反对使用它?
创建型模式如何使系统更加灵活?
二、核心概念详解
2.1 Factory Method(工厂方法)
意图:定义一个创建对象的接口,但让子类决定实例化哪个类。工厂方法将实例化推迟到子类。
适用场景:
- 一个类无法知道它必须创建的对象的具体类
- 一个类希望由子类来指定创建的对象
- 一个类将创建对象的职责委托给多个辅助子类中的某一个
// 工厂方法模式
abstract class Dialog {
// 工厂方法
abstract Button createButton();
void render() {
Button button = createButton();
button.render();
}
}
class WindowsDialog extends Dialog {
Button createButton() {
return new WindowsButton();
}
}
class WebDialog extends Dialog {
Button createButton() {
return new HTMLButton();
}
}
interface Button {
void render();
void onClick();
}
class WindowsButton implements Button {
public void render() { System.out.println("Rendering Windows button"); }
public void onClick() { System.out.println("Windows button clicked"); }
}
class HTMLButton implements Button {
public void render() { System.out.println("Rendering HTML button"); }
public void onClick() { System.out.println("HTML button clicked"); }
}
// 使用
Dialog dialog = new WindowsDialog(); // 或 new WebDialog()
dialog.render(); // 自动创建对应平台的按钮2.2 Abstract Factory(抽象工厂)
意图:提供一个创建一系列相关对象的接口,而无需指定它们的具体类。
与 Factory Method 的区别:Factory Method 创建单个产品,Abstract Factory 创建一系列相关产品(产品族)。
// 抽象工厂模式
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
class WindowsFactory implements GUIFactory {
public Button createButton() { return new WindowsButton(); }
public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}
class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
public Checkbox createCheckbox() { return new MacCheckbox(); }
}
class Application {
private Button button;
private Checkbox checkbox;
Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
void render() {
button.render();
checkbox.render();
}
}
// 使用:确保同一产品族的组件一起使用
Application app = new Application(new WindowsFactory());
app.render(); // 所有组件都是 Windows 风格2.3 Builder(建造者)
意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用场景:
- 创建复杂对象的算法应该独立于对象的组成部分
- 构造过程需要允许被构造的对象有不同的表示
// Builder 模式
class House {
private String foundation;
private String walls;
private String roof;
private boolean hasGarage;
private boolean hasSwimmingPool;
private boolean hasGarden;
// 私有构造函数
private House(Builder builder) {
this.foundation = builder.foundation;
this.walls = builder.walls;
this.roof = builder.roof;
this.hasGarage = builder.hasGarage;
this.hasSwimmingPool = builder.hasSwimmingPool;
this.hasGarden = builder.hasGarden;
}
static class Builder {
private String foundation;
private String walls;
private String roof;
private boolean hasGarage;
private boolean hasSwimmingPool;
private boolean hasGarden;
Builder(String foundation, String walls, String roof) {
this.foundation = foundation;
this.walls = walls;
this.roof = roof;
}
Builder withGarage() { this.hasGarage = true; return this; }
Builder withSwimmingPool() { this.hasSwimmingPool = true; return this; }
Builder withGarden() { this.hasGarden = true; return this; }
House build() {
return new House(this);
}
}
}
// 使用
House simpleHouse = new House.Builder("concrete", "brick", "tile")
.build();
House luxuryHouse = new House.Builder("concrete", "marble", "slate")
.withGarage()
.withSwimmingPool()
.withGarden()
.build();2.4 Prototype(原型)
意图:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
适用场景:
- 当要实例化的类是在运行时刻指定时
- 为了避免创建与产品类层次平行的工厂类层次时
- 当一个类的实例只能有几个不同状态组合中的一种时
// Prototype 模式
abstract class Shape implements Cloneable {
protected String color;
abstract Shape clone();
abstract String getInfo();
}
class Circle extends Shape {
private double radius;
Circle(String color, double radius) {
this.color = color;
this.radius = radius;
}
Shape clone() {
return new Circle(this.color, this.radius);
}
String getInfo() {
return "Circle: color=" + color + ", radius=" + radius;
}
}
class Rectangle extends Shape {
private double width, height;
Rectangle(String color, double width, double height) {
this.color = color;
this.width = width;
this.height = height;
}
Shape clone() {
return new Rectangle(this.color, this.width, this.height);
}
String getInfo() {
return "Rectangle: color=" + color + ", width=" + width + ", height=" + height;
}
}
// 使用原型注册表
class ShapeRegistry {
private Map<String, Shape> prototypes = new HashMap<>();
void register(String name, Shape shape) {
prototypes.put(name, shape);
}
Shape create(String name) {
return prototypes.get(name).clone();
}
}
// 使用
ShapeRegistry registry = new ShapeRegistry();
registry.register("red-circle", new Circle("red", 5));
registry.register("blue-rect", new Rectangle("blue", 10, 20));
Shape circle1 = registry.create("red-circle");
Shape circle2 = registry.create("red-circle"); // 复制原型2.5 Singleton(单例)
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
争议:Singleton 模式被广泛使用但也备受争议。它引入了全局状态,使得测试困难,隐藏了依赖关系。在很多场景下,依赖注入是更好的选择。
// 经典的单例实现(线程安全)
public class Database {
private static volatile Database instance;
private Connection connection;
private Database() {
// 私有构造函数,防止外部实例化
this.connection = connectToDatabase();
}
public static Database getInstance() {
if (instance == null) {
synchronized (Database.class) {
if (instance == null) {
instance = new Database();
}
}
}
return instance;
}
private Connection connectToDatabase() {
// 连接数据库
return null;
}
}
// 更现代的方式:使用枚举
public enum Database {
INSTANCE;
private Connection connection;
Database() {
this.connection = connectToDatabase();
}
public Connection getConnection() {
return connection;
}
private Connection connectToDatabase() {
return null;
}
}三、实战案例
3.1 案例:日志系统的创建
// 使用工厂方法创建不同目标的日志器
abstract class LoggerFactory {
abstract Logger createLogger();
void log(String message) {
Logger logger = createLogger();
logger.write(message);
}
}
class ConsoleLoggerFactory extends LoggerFactory {
Logger createLogger() {
return new ConsoleLogger();
}
}
class FileLoggerFactory extends LoggerFactory {
private String filePath;
FileLoggerFactory(String filePath) {
this.filePath = filePath;
}
Logger createLogger() {
return new FileLogger(filePath);
}
}
interface Logger {
void write(String message);
}
class ConsoleLogger implements Logger {
public void write(String message) {
System.out.println("[LOG] " + message);
}
}
class FileLogger implements Logger {
private String filePath;
FileLogger(String filePath) {
this.filePath = filePath;
}
public void write(String message) {
// 写入文件
}
}
// 使用
LoggerFactory factory = new ConsoleLoggerFactory();
factory.log("Application started");四、本章小结
本章介绍了 5 个创建型模式:
Factory Method:定义创建接口,让子类决定实例化哪个类
Abstract Factory:创建一系列相关产品
Builder:分步骤构建复杂对象
Prototype:通过复制原型创建新对象
Singleton:确保一个类只有一个实例
创建型模式的核心思想是:将对象的创建与使用分离,使系统更加灵活。
五、思考与练习
思考题
Factory Method 和 Abstract Factory 如何选择?
Builder 模式与直接设置属性有什么区别?
Singleton 有什么问题?有哪些替代方案?
Prototype 模式在 Java 中如何实现深复制?
编程练习
使用 Factory Method 实现一个跨平台的 UI 组件库。
使用 Builder 模式实现一个复杂的 HTTP 请求构建器。
使用 Prototype 模式实现一个游戏角色的克隆系统。