# 创建型模式

作者:Ethan.Yang
博客:https://blog.ethanyang.cn (opens new window)
相关代码参考: 设计模式代码仓库 (opens new window)


# 工厂模式

工厂模式是一种创建型设计模式,用于将对象的创建与使用解耦。它主要包括以下三种形式:简单工厂模式工厂方法模式抽象工厂模式

# 简单工厂模式

# 模式定义与角色

简单工厂模式由一个工厂类根据传入的参数决定创建哪种产品类实例。其核心角色包括:

  • **抽象产品:定义产品的公共接口。
  • **具体产品:实现抽象产品接口的具体类。
  • **工厂类:包含创建产品的方法,根据输入条件返回不同的产品实例。

# 具体案例

抽象产品接口:

public interface Car {

    String getName();
}
1
2
3
4

两个具体实现类:

public class ACar implements Car {

    @Override
    public String getName() {
        return "A";
    }
}

public class BCar implements Car {

    @Override
    public String getName() {
        return "B";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

简单工厂类(使用反射支持开闭原则):

public class CarSimpleFactory {

    public Car creat(Class<? extends Car> clazz) {
        try {
            if (null != clazz) {
                return clazz.getDeclaredConstructor().newInstance();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class Test {

    public static void main(String[] args) {
        ACar aCar = new ACar();
        BCar bCar = new BCar();

        CarSimpleFactory carSimpleFactory = new CarSimpleFactory();
        ACar acar = (ACar) carSimpleFactory.creat(ACar.class);
        BCar bcar = (BCar) carSimpleFactory.creat(BCar.class);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

✅ 优点总结:

  • 解耦调用者与具体实现类。
  • 支持扩展,新增产品时无需修改调用者代码。
  • 配合反射,便于框架和通用组件开发。

⚠️ 注意:产品类增多会导致工厂职责过重,难以维护。

# 工厂方法模式

# 模式定义与角色

工厂方法模式通过定义一个抽象工厂接口,将对象创建延迟到具体子类中,从而实现代码的开闭原则。

  • 抽象产品
  • 具体产品
  • 抽象工厂:定义创建产品的接口。
  • 具体工厂:实现创建具体产品的逻辑。

# 具体案例

抽象工厂接口:

public interface CarFactory {

    Car creat();
}
1
2
3
4

具体工厂实现类:

public class ACarFactory implements CarFactory {
    @Override
    public Car creat() {
        return new ACar();
    }
}

public class BCarFactory implements CarFactory {
    @Override
    public Car creat() {
        return new BCar();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

测试类:

public class Test {
    public static void main(String[] args) {
        CarFactory carFactory = new ACarFactory();
        Car carA = carFactory.creat();
        carA.getName();

        carFactory = new BCarFactory();
        Car carB = carFactory.creat();
        carB.getName();
    }
}
1
2
3
4
5
6
7
8
9
10
11

✅ 优点总结:

  • 完全符合开闭原则。
  • 每个产品有对应的工厂,职责清晰。
  • 扩展性强,避免了冗长的 if-else/switch

⚠️ 缺点:每新增一个产品,都需新增一个工厂类,增加系统复杂度。

# 抽象工厂模式

# 模式定义与角色

抽象工厂模式提供一个创建“一系列相关或相互依赖对象”的接口,而无需指定具体的实现类。

  • 抽象产品族接口:如引擎、轮胎。
  • 具体产品族实现:A 品牌的引擎/轮胎,B 品牌的引擎/轮胎。
  • 抽象工厂接口:定义生产多个产品的方法。
  • 具体工厂:负责创建具体产品族的所有组件。

# 具体案例

产品族抽象(引擎 & 轮胎):

// 引擎接口
public interface Engine {
    String getEngineName();
}

// 轮胎接口
public interface Tyre {
    String getTyreName();
}

1
2
3
4
5
6
7
8
9
10

产品族具体实现(A品牌 & B品牌):

public class AEngine implements Engine {
    @Override
    public String getEngineName() {
        return "AEngine";
    }
}

public class ATyre implements Tyre {

    @Override
    public String getTyreName() {
        return "A牌轮胎";
    }
}

public class BEngine implements Engine {
    @Override
    public String getEngineName() {
        return "B牌引擎";
    }
}

public class BTyre implements Tyre {
    @Override
    public String getTyreName() {
        return "B牌轮胎";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

抽象工厂接口(定义如何创建一整套组件):

public interface CarFactory {
    Engine createEngine();

    Tyre createTyre();
}
1
2
3
4
5

具体工厂实现类(A品牌、B品牌):

public class ACarFactory implements CarFactory {
    @Override
    public Engine createEngine() {
        return new AEngine();
    }

    @Override
    public Tyre createTyre() {
        return new ATyre();
    }
}

public class BCarFactory implements CarFactory {
    @Override
    public Engine createEngine() {
        return new BEngine();
    }

    @Override
    public Tyre createTyre() {
        return new BTyre();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 单例模式

单例模式确保一个类在系统中只有一个实例,并提供一个全局访问点。常用于管理全局资源,如配置对象、线程池、缓存、数据库连接等。

# 饿汉式单例

饿汉式在类加载时即创建单例对象,因此天然线程安全,但不能实现延迟加载,即使未使用也会初始化实例。

方式一:静态成员变量

public class HungrySingleton {

    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11

方式二:静态代码块初始化

public class HungrySingleton1 {

    private static final HungrySingleton1 hungrySingleton1;

    static {
        hungrySingleton1 = new HungrySingleton1();
    }

    private HungrySingleton1() {
    }

    public static HungrySingleton1 getInstance() {
        return hungrySingleton1;
    } 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 懒汉式单例

懒汉式在第一次调用 getInstance() 时才创建实例,可实现延迟加载,但需要处理线程安全问题

非线程安全版本(不可用)

public class LazySimpleSingleton {

    private static LazySimpleSingleton lazySimpleSingleton = null;

    private LazySimpleSingleton() {
    }

    public static LazySimpleSingleton getInstance() {
        if (lazySimpleSingleton == null) {
            // 线程安全问题
            lazySimpleSingleton = new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

这种写法, 在多线程的情况下会有线程安全问题, 比如A B线程同时进入 1. 处, 便会创建多个实例, 这时候一般解决方案加锁

synchronized 加锁

public class LazySimpleSingleton {

    private static LazySimpleSingleton lazySimpleSingleton = null;

    private LazySimpleSingleton() {
    }

    public synchronized static LazySimpleSingleton getInstance() {
        if (lazySimpleSingleton == null) {
            // 线程安全问题
            lazySimpleSingleton = new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

如果并发量过高, 会导致线程阻塞, 此时可以通过双重检测方式进一步优化

双重检查锁(推荐)

public class LazySimpleSingleton {

    private volatile static LazySimpleSingleton lazySimpleSingleton = null;

    private LazySimpleSingleton() {
    }

    public static LazySimpleSingleton getInstance() {
        if (lazySimpleSingleton == null) {
            synchronized (LazySimpleSingleton.class) {
                if (lazySimpleSingleton == null) {
                    // 线程安全问题
                    lazySimpleSingleton = new LazySimpleSingleton();
                }
            }

        }
        return lazySimpleSingleton;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

这种优化方式实际上解决了lazySimpleSingleton!=null时 直接return, 而不是线程被阻塞, 但是如果多个线程在lazySimpleSingleton==null时, 依旧会阻塞, 因为用了synchronized关键字, 采用静态内部类进行升级

静态内部类(推荐,线程安全 + 延迟加载)

public class LazyInnerClassSingleton {

    // static 保证该方法不被重写 重载
    private static class LazyHolder {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }

    public static final LazyInnerClassSingleton getInstance() {
        return LazyHolder.LAZY;
    }

    // 使用 LazyInnerClassSingleton 时会先初始化内部类
    private LazyInnerClassSingleton() {
        // 防止使用反射调用构造器方法生成实例, 破坏单例
        if (LazyHolder.LAZY != null) {
            throw new RuntimeException("不允许创建多个实例");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

该方式利用类加载机制确保线程安全,且只有在调用 getInstance() 时才加载 LazyHolder 类,兼具懒加载和高效。

# 注册式单例模式

注册式单例将每个实例登记到一个容器中,可以根据 key 获取对应的单例对象。

# 枚举式单例模式

public enum EnumSingleton {

    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

枚举天然支持序列化机制,且反射无法破坏单例,是实现单例最安全、最简单的方式。唯一缺点是无法延迟加载。

# 容器式单例模式

public class ContainerSingleton {

    private ContainerSingleton() {
    }
    private static Map<String, Object> ioc = new ConcurrentHashMap<>();
    public static Object getBean(String className) {
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object obj = null;
                try {
                    obj = Class.forName(className).getConstructor().newInstance();
                    ioc.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(className);
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

容器式适用于管理多个类型的单例对象,类似于简单的 IOC 容器。

# 原型模式

原型模式是一种创建型设计模式,它通过拷贝现有对象来创建新对象,而不是通过实例化类来创建。这种方式 跳过了复杂的构造过程,尤其适合对象创建成本较高的场景。

# 模式定义与角色

原型模式通过拷贝已有实例来生成新对象,不经过构造函数调用。它主要包含以下角色:

  • Client(客户类):调用克隆方法创建新对象。
  • Prototype(抽象原型类):定义克隆方法。
  • ConcretePrototype(具体原型类):实现克隆方法,定义要复制的字段。

# 原型模式的实现

自定义克隆接口

public interface IPrototype<T> {
    T clone();
}
1
2
3

实现类(浅克隆)

public class ConcretePrototype implements IPrototype{

    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public ConcretePrototype clone() {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAge(this.age);
        concretePrototype.setName(this.name);
        return concretePrototype;
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

JDK提供了Cloneable接口, 基于该接口进行改造并增加引用类型

使用 JDK 的 Cloneable 接口(浅克隆)

@Data
public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;

    private List<String> hobbies;

    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

测试

public class Test {

    public static void main(String[] args) {
        // 创建原型对象
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAge(18);
        concretePrototype.setName("tom");
        List<String> hobbies = new ArrayList<>();
        hobbies.add("java");
        hobbies.add("python");
        concretePrototype.setHobbies(hobbies);
        // 拷贝原型对象
        ConcretePrototype clone = concretePrototype.clone();
        clone.getHobbies().add("clone");
        System.out.println("原型对象: " + concretePrototype);
        System.out.println("克隆对象: " + clone);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

✅ 结果说明:hobbies 是引用类型,因此浅克隆仅复制引用地址,原型对象和克隆对象共享同一个集合,修改其中一个,另一个也会被影响。

深克隆(基于序列化)

public class DeepClonePrototype implements Cloneable, Serializable {

    private int age;
    private String name;

    private List<String> hobbies;

    @Override
    protected DeepClonePrototype clone() throws CloneNotSupportedException {
        try {
            return (DeepClonePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public DeepClonePrototype deepClone() {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (DeepClonePrototype)ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String toString() {
        return "DeepClonePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

注意: 深克隆可完全复制引用类型的数据结构,但也存在副作用,例如破坏单例模式。因此在使用时应结合实际情况权衡利弊。

# 建造者模式

建造者模式是一种创建型设计模式,它的核心思想是将一个复杂对象的构建过程与它的表示分离,使得相同的构建过程可以创建出不同的对象表示,避免构造函数参数过多造成的混乱。

# 模式定义与角色

建造者模式包含以下四个角色:

  1. Product(产品类):被构建的复杂对象,包含多个组成部分。
  2. Builder(抽象建造者):定义创建产品各部分的抽象方法。
  3. ConcreteBuilder(具体建造者):实现构建细节,组装各部分。
  4. Director(指挥者):负责调用建造者的各个方法,完成产品的构建流程。

# 建造者模式的实现

public class CourseBuilder {

    @Data
    public class Course {

        private String name;
        private String ppt;
        private String video;
        private String note;

        @Override
        public String toString() {
            return "Course{" +
                    "name='" + name + '\'' +
                    ", ppt='" + ppt + '\'' +
                    ", video='" + video + '\'' +
                    ", note='" + note + '\'' +
                    '}';
        }
    }

    private Course course = new Course();

    public CourseBuilder addName(String name) {
        course.setName(name);
        return this;
    }

    public CourseBuilder addPpt(String ppt) {
        course.setPpt(ppt);
        return this;
    }

    public CourseBuilder addVideo(String video) {
        course.setVideo(video);
        return this;
    }

    public CourseBuilder addNote(String note) {
        course.setNote(note);
        return this;
    }

    public Course build() {
        return this.course;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Test {

    public static void main(String[] args) {
        CourseBuilder courseBuilder = new CourseBuilder()
                .addName("name")
                .addPpt("ppt")
                .addVideo("video")
                .addNote("note");
        System.out.println(courseBuilder.build());
    }
}
1
2
3
4
5
6
7
8
9
10
11

✅ 结果说明

采用链式编程方式依次调用构建方法,最后通过 build() 返回构建完成的复杂对象,逻辑清晰、易于扩展。