2025.12.12
1. 工厂方法模式
-
核心意图:定义一个用于创建对象的接口,但让子类决定将哪一个类实例化。工厂方法模式使一个类的实例化延迟到其子类。
-
解决的问题:依赖具体类:在代码中直接
new一个具体对象,使得客户端代码严重依赖于具体类。一旦需要更换对象,修改起来非常麻烦。违反开闭原则:当需要增加新的产品类型时,必须修改创建对象的源代码。 -
解决方案:抽象产品:定义产品的共同接口。具体产品:实现抽象产品接口的不同类。抽象工厂:声明创建产品的工厂方法,该方法返回一个抽象产品类型。具体工厂:继承抽象工厂,重写工厂方法,以创建并返回一个具体产品的实例。
代码示例(基于您提供的资料):
// 1. 抽象产品
public abstract class Fruit {
public abstract String getName();
}
// 2. 具体产品
public class Apple extends Fruit {
@Override
public String getName() { return "苹果"; }
}
public class Orange extends Fruit {
@Override
public String getName() { return "橘子"; }
}
// 3. 抽象工厂
public abstract class FruitFactory {
public abstract Fruit createFruit();
}
// 4. 具体工厂
public class AppleFactory extends FruitFactory {
@Override
public Fruit createFruit() {
return new Apple(); // 苹果工厂专门生产苹果
}
}
public class OrangeFactory extends FruitFactory {
@Override
public Fruit createFruit() {
return new Orange(); // 橘子工厂专门生产橘子
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
FruitFactory factory = new AppleFactory(); // 想要苹果,就使用苹果工厂
Fruit fruit = factory.createFruit(); // 创建对象的过程被封装
System.out.println(fruit.getName());
}
}
-
优点:符合开闭原则。当需要增加新产品(如
Peach)时,只需新增Peach类和PeachFactory类,无需修改任何现有代码。 -
缺点:每增加一个产品,就需要增加一个具体工厂类,会导致类的个数增多,系统变复杂。
2. 抽象工厂模式
- 核心意图:提供一个接口,用于创建相关的或相互依赖的对象家族,而无需明确指定具体类。
- 解决的问题:工厂方法模式每个工厂只生产一种产品。但当产品是一个家族(例如,小米家族:小米手机、小米平板、小米路由器)时,我们需要确保创建的对象是相互兼容的(不能用小米手机配华为平板)。工厂方法模式需要为每个产品都创建一个工厂,非常繁琐。
- 解决方案:抽象工厂:声明一组用于创建一族产品的方法,每个方法返回一个抽象产品。具体工厂:实现抽象工厂接口,负责创建一个特定产品族的具体产品。抽象产品:为每种产品类型声明接口。具体产品:实现抽象产品接口,定义由具体工厂创建的产品对象。
- 图解(对应您链接4的图片):
AbstractFactory是抽象工厂,它定义了创建Phone,Table,Router的方法。XiaomiFactory和HuaweiFactory是具体工厂,它们分别创建小米家族和华为家族的全部产品。XiaomiPhone和HuaweiPhone等都是具体产品。 - 优点:保证产品族兼容性:客户端一次只能使用一个产品族中的产品。易于切换产品族:要更换产品族(如从小米换到华为),只需切换具体工厂实例。
- 缺点:难以扩展新的产品等级。如果要在产品族中增加一个新产品(例如
SmartWatch),就需要修改AbstractFactory接口,这会导致所有具体工厂类都需要修改,违反了开闭原则。
3. 建造者模式
-
核心意图:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
-
解决的问题:当一个类的构造函数参数过多(例如您例子中的
Student类),或者参数的构建顺序和逻辑复杂时,直接调用构造函数会非常笨拙且容易出错(“重叠构造函数”和“JavaBeans模式”都有其缺点)。 -
解决方案:产品:要被构建的复杂对象。抽象建造者:为创建一个产品对象的各个部件指定抽象接口。具体建造者:实现抽象建造者接口,定义具体的构建过程和提供产品的返回方法。指挥者:负责安排复杂对象的构建次序。建造过程通常被稳定地封装在指挥者中。客户端:创建指挥者和建造者,让指挥者负责构建过程。
代码示例(简化自您提供的资料):
// 1. 产品
public class Student {
private int id;
private String name;
// ... 其他属性
// 构造函数私有,只能通过建造者创建
private Student(StudentBuilder builder) {
this.id = builder.id;
this.name = builder.name;
// ... 设置其他属性
}
// 2. 将建造者作为静态内部类(这是一种常见的变体,省略了指挥者)
public static class StudentBuilder {
private int id;
private String name;
public StudentBuilder id(int id) {
this.id = id;
return this; // 返回this,支持链式调用
}
public StudentBuilder name(String name) {
this.name = name;
return this;
}
// ... 其他属性的设置方法
public Student build() {
// 这里可以添加校验逻辑
return new Student(this); // 最终构建产品对象
}
}
}
// 客户端使用
Student student = new Student.StudentBuilder()
.id(1)
.name("小明")
// ... 设置其他属性
.build(); // build()方法完成了构建过程
- 优点:构建过程清晰:可以将复杂对象的构建步骤分解,使代码更易读和维护。灵活性强:相同的构建过程可以创建不同的产品(通过切换具体建造者)。很好的控制构建过程:指挥者固定了构建流程,具体建造者实现细节。
4. 单例模式
-
核心意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
-
解决的问题:有些类(如数据库连接池、线程池、配置管理器等)只需要一个实例来协调系统行为。如果存在多个实例,会导致资源浪费或行为异常。
-
解决方案:将类的构造函数私有化,防止外部通过
new创建实例。在类内部创建唯一的实例。提供一个公共的静态方法,让外部可以获取这个唯一实例。
常见实现方式:
**饿汉式**:类加载时就创建实例,线程安全,但可能造成资源浪费。
```
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
```
**懒汉式(双重检查锁定)**:延迟加载,只在第一次使用时创建实例,且保证线程安全。
```
public class Singleton {
private static volatile Singleton INSTANCE; // volatile 保证可见性和禁止指令重排序
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) { // 第一次检查
synchronized (Singleton.class) {
if (INSTANCE == null) { // 第二次检查
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
```
**静态内部类**:利用类加载机制保证线程安全,同时实现延迟加载,是推荐的一种写法。
```
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE; // 此时才会加载Holder类并创建INSTANCE
}
}
```
-
优点:严格控制实例的创建和访问。
-
缺点:在一定程度上违反了单一职责原则(因为类自身要负责管理自己的实例),并且对单元测试不友好。
5. 原型模式
-
核心意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
-
解决的问题:当直接创建对象的成本较高时(例如,对象需要经过复杂的数据库操作、计算才能初始化),复制一个已有对象可以大大提高性能。
-
解决方案:实现
Cloneable接口(在Java中),并重写clone()方法。关键点在于区分浅拷贝和深拷贝。浅拷贝:只复制对象本身和其中的基本数据类型,对于引用类型,只复制引用地址,新旧对象共享引用成员。深拷贝:不仅复制对象本身,连其所有的引用成员也全部复制一份,生成一个完全独立的新对象。
代码示例:
public class PrototypeDemo implements Cloneable {
private String name;
private List<String> list;
public PrototypeDemo(String name, List<String> list) {
this.name = name;
this.list = list;
}
// 浅拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 默认的clone()是浅拷贝
}
// 深拷贝
public PrototypeDemo deepClone() throws CloneNotSupportedException {
PrototypeDemo copy = (PrototypeDemo) super.clone();
copy.name = new String(this.name); // 对String等不可变对象,也可直接赋值
copy.list = new ArrayList<>(this.list); // 创建新的List,实现深拷贝
return copy;
}
}
-
优点:性能高:逃避了构造函数的约束,复制比新建一个对象性能更高。简化创建过程:可以动态地保存和恢复对象的配置。
-
缺点:必须实现克隆方法,并且当对象有复杂的嵌套引用时,实现深拷贝会比较复杂。
总结对比
| 模式名 | 核心焦点 | 适用场景 |
|---|---|---|
| 工厂方法 | 单个产品的创建,将实例化推迟到子类 | 不关心具体产品类,但需要明确不同创建逻辑 |
| 抽象工厂 | 相关产品家族的创建 | 系统需要多个产品组合,并保证它们来自同一家族 |
| 建造者 | 复杂对象的分步构建 | 对象有很多属性,构建过程复杂,希望构建代码清晰 |
| 单例 | 类的唯一实例 | 需要严格控制全局只有一个实例的场景 |
| 原型 | 通过拷贝已有对象来创建新对象 | 创建新对象成本较高,且与已有对象相似 |
结构型设计模式详解
1. 适配器模式 (Adapter Pattern)
核心思想:将一个类的接口转换成客户端期望的另一个接口
两种实现方式:
- 类适配器:通过继承实现
public class TestAdapter extends TestSupplier implements Target {
@Override
public String supply() {
return super.doSupply();
}
}
- 对象适配器:通过组合实现(推荐)
public class TestAdapter implements Target {
TestSupplier supplier;
public TestAdapter(TestSupplier supplier) {
this.supplier = supplier;
}
@Override
public String supply() {
return supplier.doSupply();
}
}
现实例子:电源适配器、Type-C扩展坞
2. 桥接模式 (Bridge Pattern)
核心思想:将抽象部分与实现部分分离,使它们可以独立变化
解决什么问题:
- 避免类爆炸(如奶茶类型×杯型×配料组合过多)
- 将多个维度独立变化
实现方式:
public abstract class AbstractTea {
protected Size size; // 桥接的关键
protected AbstractTea(Size size) {
this.size = size;
}
public abstract String getType();
}
3. 组合模式 (Composite Pattern)
核心思想:将对象组合成树形结构以表示"部分-整体"的层次结构
适用场景:
- 文件系统管理
- 菜单树形结构
- 任何需要递归处理的层次结构
组件类型:
- Component:抽象组件
- Leaf:叶子节点(如File)
- Composite:复合组件(如Directory)
4. 装饰模式 (Decorator Pattern)
核心思想:动态地给对象添加额外的职责,但不改变其接口
特点:
- 装饰者和被装饰者实现相同接口
- 强调增强对象本身的功能
- 可以嵌套装饰
public class DecoratorImpl extends Decorator {
public DecoratorImpl(Base base) {
super(base);
}
@Override
public void test() {
System.out.println("前置增强");
super.test();
System.out.println("后置增强");
}
}
5. 代理模式 (Proxy Pattern)
核心思想:为其他对象提供一种代理以控制对这个对象的访问
与装饰模式的区别:
- 装饰模式:增强自身功能
- 代理模式:控制访问,添加与业务无关的功能(如日志、缓存)
实现方式:
- 静态代理
- JDK动态代理(基于接口)
- CGLib动态代理(基于继承)
6. 外观模式 (Facade Pattern)
核心思想:为子系统中的一组接口提供一个一致的界面
优点:
- 简化客户端与子系统的交互
- 降低系统耦合度
- 符合迪米特法则(最少知识原则)
例子:结婚服务门面(整合排队、结婚、领证子系统)
7. 享元模式 (Flyweight Pattern)
核心思想:运用共享技术有效地支持大量细粒度的对象
适用场景:
- 大量相似对象的场景
- 对象的大部分状态可以外部化
- 需要缓冲池的场景
Java中的例子:String常量池
设计模式对比总结
| 模式 | 核心目的 | 关键特点 |
|---|---|---|
| 适配器 | 接口转换 | 解决接口不兼容问题 |
| 桥接 | 分离抽象与实现 | 避免类爆炸,多维度变化 |
| 组合 | 树形结构处理 | 部分-整体层次结构 |
| 装饰 | 动态增强功能 | 不改变接口,功能增强 |
| 代理 | 控制访问 | 访问控制,非业务功能 |
| 外观 | 简化接口 | 统一入口,降低耦合 |
| 享元 | 对象共享 | 减少内存占用,共享细粒度对象 |

浙公网安备 33010602011771号