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的方法。XiaomiFactoryHuaweiFactory是具体工厂,它们分别创建小米家族和华为家族的全部产品。XiaomiPhoneHuaweiPhone等都是具体产品。
  • 优点保证产品族兼容性:客户端一次只能使用一个产品族中的产品。易于切换产品族:要更换产品族(如从小米换到华为),只需切换具体工厂实例。
  • 缺点难以扩展新的产品等级。如果要在产品族中增加一个新产品(例如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常量池

设计模式对比总结

模式 核心目的 关键特点
适配器 接口转换 解决接口不兼容问题
桥接 分离抽象与实现 避免类爆炸,多维度变化
组合 树形结构处理 部分-整体层次结构
装饰 动态增强功能 不改变接口,功能增强
代理 控制访问 访问控制,非业务功能
外观 简化接口 统一入口,降低耦合
享元 对象共享 减少内存占用,共享细粒度对象
posted @ 2025-12-12 23:30  臧博涛  阅读(0)  评论(0)    收藏  举报