设计模式:创建型(Creational)
简单工厂模式(Simple Factory Pattern)

1 public interface Computer { 2 3 void setComputerSystem(); 4 5 } 6 7 public class XiaoMiComputer implements Computer { 8 9 @Override 10 public void setComputerSystem() { 11 System.out.println("XiaoMiComputer"); 12 } 13 14 } 15 16 public class MacComputer implements Computer { 17 18 @Override 19 public void setComputerSystem() { 20 System.out.println("MacComputer"); 21 } 22 23 } 24 25 public class ComputerFactory { 26 27 public static Computer makeComputer(String brand) { 28 if (brand == null) { 29 return null; 30 } 31 32 Computer computer = null; 33 34 if (brand.equalsIgnoreCase("XiaoMi")) { 35 computer = new XiaoMiComputer(); 36 } else if (brand.equalsIgnoreCase("apple")) { 37 computer = new MacComputer(); 38 } 39 40 return computer; 41 } 42 43 } 44 45 public class UseSimpleFactory { 46 47 public static void main(String[] args) { 48 Computer computer = ComputerFactory.makeComputer("XiaoMi"); 49 computer.setComputerSystem(); 50 51 computer = ComputerFactory.makeComputer("apple"); 52 computer.setComputerSystem(); 53 } 54 55 }
解决的问题:(符合了开闭原则,里式替换原则,依赖倒置原则,接口隔离原则)实现了对责任的分割,它提供了专门的工厂类用于创建对象,便于维护。应用中比如反射:
1 public class BeanFactory { 2 3 public static Object createBean(String classname) throws Exception { 4 Class<?> clazz = Class.forName(classname); 5 Object o = clazz.newInstance(); 6 return o; 7 } 8 9 }
优点:一个调用者想创建一个对象,只要知道其名称就可以了。屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:违反了开闭原则,每当我们增加一种产品的时候就要去修改工厂方法。违反了单一职责原则,一个工厂类中负责创建的产品种类过多。
工厂方法模式(Factory Method Pattern)

1 public interface Computer { 2 3 void setComputerSystem(); 4 5 } 6 7 public class XiaoMiComputer implements Computer { 8 9 @Override 10 public void setComputerSystem() { 11 System.out.println("XiaoMiComputer"); 12 } 13 14 } 15 16 public class MacComputer implements Computer { 17 18 @Override 19 public void setComputerSystem() { 20 System.out.println("MacComputer"); 21 } 22 23 } 24 25 public class XiaoMiComputerFactory { 26 27 public static Computer makeComputer() { 28 return new XiaoMiComputer(); 29 } 30 31 } 32 33 public class MacComputerFactory { 34 35 public static Computer makeComputer() { 36 return new MacComputer(); 37 } 38 39 } 40 41 public class UseFactoryMethodPattern { 42 43 public static void main(String[] args) { 44 Computer computer = XiaoMiComputerFactory.makeComputer(); 45 computer.setComputerSystem(); 46 47 computer = MacComputerFactory.makeComputer(); 48 computer.setComputerSystem(); 49 } 50 51 }
解决的问题:(符合了开闭原则,单一职责原则,里式替换原则,依赖倒置原则,接口隔离原则)让对象创建的职责更加清晰。
优点:在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
缺点:每增加一种产品就要相应的增加一个工厂类,类增多了。
在 JDK 源码中应用的示例:
在 Collection 接口中的 iterator() 方法中,使用的是工厂方法模式来创建 Iterator 对象。具体实现细节如下:
1 public interface Collection<E> extends Iterable<E> { 2 // ... 3 4 Iterator<E> iterator(); 5 6 // ... 7 }
在 JDK 中,针对不同的 Collection 类型,会有不同的具体实现类来实现 iterator() 方法。例如,ArrayList 类的 iterator() 方法的实现如下:
1 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { 2 // ... 3 4 public Iterator<E> iterator() { 5 return new Itr(); 6 } 7 8 // ... 9 10 private class Itr implements Iterator<E> { 11 // ... 12 } 13 }
在这个实现中,ArrayList 类实现了 Collection 接口,并覆盖了 iterator() 方法。在 iterator() 方法中,它创建了一个 Itr 对象,并将其作为 Iterator 对象返回。这个 Itr 对象是一个内部类,实现了 Iterator 接口,并提供了遍历 ArrayList 元素的方法。
不同的 Collection 类型,其 iterator() 方法的实现方式可能不同,但它们都遵循了工厂方法模式的基本原则,即将对象的创建过程封装起来,并提供一个公共的接口来创建对象,使得客户端代码无需关心对象的具体类型。通过这种方式,可以实现对不同 Collection 类型的遍历操作,同时保持代码的可扩展性和灵活性。
抽象工厂模式(Abstract Factory Pattern)

1 public interface Computer { 2 3 void setComputerSystem(); 4 5 } 6 7 public class XiaoMiComputer implements Computer { 8 9 @Override 10 public void setComputerSystem() { 11 System.out.println("XiaoMiComputerOS"); 12 } 13 14 } 15 16 public class MacComputer implements Computer { 17 18 @Override 19 public void setComputerSystem() { 20 System.out.println("MacComputerOS"); 21 } 22 23 } 24 25 public interface MobilePhone { 26 27 void setOperationSystem(); 28 29 } 30 31 public class XiaoMiPhone implements MobilePhone { 32 33 @Override 34 public void setOperationSystem() { 35 System.out.println("XiaoMiPhoneOS"); 36 } 37 38 } 39 40 public class iPhone implements MobilePhone { 41 42 @Override 43 public void setOperationSystem() { 44 System.out.println("iPhoneOS"); 45 } 46 47 } 48 49 public interface AbstractFactory { 50 51 Computer makeComputer(); 52 MobilePhone makeMobilePhone(); 53 54 } 55 56 57 public class XiaoMiFactory implements AbstractFactory { 58 59 @Override 60 public Computer makeComputer() { 61 return new XiaoMiComputer(); 62 } 63 64 @Override 65 public MobilePhone makeMobilePhone() { 66 return new XiaoMiPhone(); 67 } 68 69 } 70 71 public class AppleFactory implements AbstractFactory { 72 73 @Override 74 public Computer makeComputer() { 75 return new MacComputer(); 76 } 77 78 @Override 79 public MobilePhone makeMobilePhone() { 80 return new iPhone(); 81 } 82 83 } 84 85 86 public class FactoryProducer { 87 88 public static AbstractFactory getFactory(String brand) { 89 if (brand == null) { 90 return null; 91 } 92 93 if (brand.equalsIgnoreCase("Apple")) { 94 return new AppleFactory(); 95 } else if (brand.equalsIgnoreCase("XiaoMi")) { 96 return new XiaoMiFactory(); 97 } 98 99 return null; 100 } 101 102 } 103 104 public class UseAbstractFactoryPattern { 105 106 public static void main(String[] args) { 107 AbstractFactory appleFactory = FactoryProducer.getFactory("Apple"); 108 Computer computer = appleFactory.makeComputer(); 109 computer.setComputerSystem(); 110 MobilePhone mobilePhoneFactory = appleFactory.makeMobilePhone(); 111 mobilePhoneFactory.setOperationSystem(); 112 113 AbstractFactory xiaoMiFactory = FactoryProducer.getFactory("XiaoMi"); 114 computer = xiaoMiFactory.makeComputer(); 115 computer.setComputerSystem(); 116 mobilePhoneFactory = xiaoMiFactory.makeMobilePhone(); 117 mobilePhoneFactory.setOperationSystem(); 118 } 119 120 }
解决的问题:
-
提高代码的可维护性和可扩展性:通过使用抽象工厂模式,我们可以将对象的创建过程集中在一个工厂中,并通过继承或实现抽象工厂来创建不同类型的对象。这样,如果需要添加新的产品家族,只需要创建新的具体工厂和产品类,并实现对应的抽象接口即可。
-
隐藏对象的创建细节:客户端只需要与抽象工厂和产品接口交互,而不需要知道具体的产品实现细节。这样,我们可以隐藏对象的创建细节,提高代码的安全性和可靠性。
-
提高代码的灵活性:通过使用抽象工厂模式,我们可以动态地切换产品家族,从而提高代码的灵活性。例如,我们可以通过配置文件或运行时参数来选择具体的工厂实现,从而创建不同的产品家族。
在 JDK 源码中有许多应用的一些示例:
-
javax.xml.parsers.DocumentBuilderFactory 和 javax.xml.transform.TransformerFactory 这两个类都是抽象工厂,它们的具体实现可以用于创建不同类型的 XML 解析器和转换器。
-
java.sql.DriverManager 是一个工厂类,它提供了一系列静态方法来创建和管理数据库连接。其中,getConnection 方法就是一个抽象工厂方法,它根据传入的数据库 URL 和属性创建不同类型的数据库连接。
-
java.awt.Toolkit 是一个抽象工厂,它提供了创建各种图形界面组件的方法,例如创建窗口、标签、按钮等。具体的组件实现由不同的工厂类提供,例如 WindowsToolkit 和 UnixToolkit。
建造者模式(Builder Pattern)
传统建造者模式:


1 public class PersonalComputer { 2 3 private String cpu; 4 private String ram; 5 private int usbCount; 6 private String keyboard; 7 private String display; 8 9 public PersonalComputer(Builder builder) { 10 this.cpu = builder.cpu; 11 this.ram = builder.ram; 12 this.usbCount = builder.usbCount; 13 this.keyboard = builder.keyboard; 14 this.display = builder.display; 15 } 16 17 public static class Builder { 18 private String cpu; 19 private String ram; 20 private int usbCount; 21 private String keyboard; 22 private String display; 23 24 public Builder(String cpu, String ram) { 25 this.cpu = cpu; 26 this.ram = ram; 27 } 28 29 public Builder setUsbCount(int usbCount) { 30 this.usbCount = usbCount; 31 return this; 32 } 33 34 public Builder setKeyboard(String keyboard) { 35 this.keyboard = keyboard; 36 return this; 37 } 38 39 public Builder setDisplay(String display) { 40 this.display = display; 41 return this; 42 } 43 44 public PersonalComputer build() { 45 return new PersonalComputer(this); 46 } 47 48 } 49 50 @Override 51 public String toString() { 52 return "PersonalComputer{" + 53 "cpu='" + cpu + '\'' + 54 ", ram='" + ram + '\'' + 55 ", usbCount=" + usbCount + 56 ", keyboard='" + keyboard + '\'' + 57 ", display='" + display + '\'' + 58 '}'; 59 } 60 61 }

1 public class Computer { 2 3 private String cpu; 4 private String ram; 5 private int usbCount; 6 private String keyboard; 7 private String display; 8 9 public Computer(String cpu, String ram) { 10 this.cpu = cpu; 11 this.ram = ram; 12 } 13 14 public void setUsbCount(int usbCount) { 15 this.usbCount = usbCount; 16 } 17 18 public void setKeyboard(String keyboard) { 19 this.keyboard = keyboard; 20 } 21 22 public void setDisplay(String display) { 23 this.display = display; 24 } 25 26 @Override 27 public String toString() { 28 return "Computer{" + 29 "cpu='" + cpu + '\'' + 30 ", ram='" + ram + '\'' + 31 ", usbCount=" + usbCount + 32 ", keyboard='" + keyboard + '\'' + 33 ", display='" + display + '\'' + 34 '}'; 35 } 36 } 37 38 39 public interface ComputerBuilder { 40 41 void setUsbCount(); 42 43 void setKeyboard(); 44 45 void setDisplay(); 46 47 Computer getComputer(); 48 49 } 50 51 52 public class ComputerDirector { 53 54 public void makeComputer(ComputerBuilder builder) { 55 builder.setUsbCount(); 56 builder.setKeyboard(); 57 builder.setDisplay(); 58 } 59 60 } 61 62 63 public class MacComputerBuilder implements ComputerBuilder { 64 private Computer computer; 65 66 public MacComputerBuilder(String cpu, String ram) { 67 computer = new Computer(cpu, ram); 68 } 69 70 @Override 71 public void setUsbCount() { 72 computer.setUsbCount(4); 73 } 74 75 @Override 76 public void setKeyboard() { 77 computer.setKeyboard("苹果键盘"); 78 } 79 80 @Override 81 public void setDisplay() { 82 computer.setDisplay("苹果显示器"); 83 } 84 85 @Override 86 public Computer getComputer() { 87 return computer; 88 } 89 90 }
最常用的建造者模式是通过链式调用的方式实现的。
解决的问题:1. 从客户端程序传递到 Factory 类的参数太多,这很容易出错,因为大多数时候,参数的类型是相同的,并且从客户端很难维护参数的顺序。2. 某些参数可能是可选的,但在工厂模式中,我们被迫定义所有参数。避免在创建对象时出现复杂的参数传递问题。
在 JDK 源码中有许多应用的一些示例:
-
java.net.http.HttpRequest 和 java.net.http.HttpResponse:这两个类分别表示 HTTP 请求和响应,它们的 Builder 模式实现可以帮助用户方便地构建 HTTP 请求和响应对象,并且可以使用链式调用方式来设置不同的属性。
- java.lang.StringBuilder 和 java.lang.StringBuffer:这两个类都是可变字符串类,它们内部都使用了 Builder 模式来创建字符串。
单例模式(Singleton Pattern)
双重检查锁单例模式:
1 public class Singleton { 2 private static volatile Singleton instance; 3 4 private Singleton() {} 5 6 public static Singleton getInstance() { 7 // 在访问该实例的方法中,首先判断该实例是否已经创建,如果没有创建,则加锁并再次判断 8 if (instance == null) { 9 synchronized (Singleton.class) { 10 if (instance == null) { 11 instance = new Singleton(); 12 } 13 } 14 } 15 return instance; 16 } 17 }
懒汉式单例模式的同步范围是整个getInstance()方法,即每次获取实例时都需要获得锁,这样会导致在高并发的情况下,线程需要等待其他线程释放锁,从而降低了性能。而双重检查锁单例模式的同步范围仅限于getInstance()方法中的部分代码块,只有在instance为null时才需要获得锁,这样可以避免在多数情况下的性能损失。此外,双重检查锁单例模式通过使用volatile关键字来保证instance的可见性,从而避免了在某些JVM中由于JVM重排序优化带来的线程安全问题。
静态内部类实现单例模式:
1 public class Singleton { 2 3 private static class SingletonInstance { 4 private final static Singleton INSTANCE = new Singleton(); 5 } 6 7 public static Singleton getInstance() { 8 return SingletonInstance.INSTANCE; 9 } 10 11 }
解决的问题:在某些场景下,我们需要确保系统中某个类只有一个实例存在,例如:线程池、数据库连接池、配置信息等等。如果不使用单例模式来管理这些类的实例,就可能会出现资源浪费、性能下降、数据不一致等问题。使用单例模式可以确保系统中某个类只有一个实例,从而节省系统资源,提高性能,确保数据一致性。同时,单例模式也提供了一个全局的访问点来访问这个唯一实例,方便了系统中其他模块的调用和使用。
在 JDK 源码中有许多应用的一些示例:
-
java.lang.System: System类是单例模式,它提供了与系统相关的信息,例如系统时间、系统属性等。
-
java.util.Calendar: Calendar类是单例模式,它是一个日历类,提供了与日期时间相关的一系列操作。
反射问题:单例模式可以通过在构造函数中增加判断来防止通过反射方式创建多个实例。一种常见的方法是在构造函数中添加一个私有的flag变量来记录单例是否已经被创建,如果已经被创建,则抛出异常,防止反射创建新的实例。
1 private static boolean flag = false; 2 3 private Singleton() { 4 synchronized (Singleton.class) { 5 if (flag == false) { 6 flag = true; 7 } else { 8 throw new IllegalStateException("Already initialized."); 9 } 10 } 11 // 初始化操作 12 }
序列化问题:在单例类中实现 readResolve()
方法,该方法返回单例对象,以确保反序列化后获得的对象是同一个单例对象。具体实现方法是将 readResolve()
方法的访问修饰符设为 private
,然后返回单例对象即可。
原型模式(Prototype Pattern)
一个经典的原型模式的例子是通过复制一个已有的对象来创建一个新的对象,例如Java中的Cloneable接口和Object类中的clone()方法。假设我们有一个Student类,它有一些基本属性,例如姓名、年龄和学号等
1 public class Student implements Cloneable { 2 private String name; 3 private int age; 4 private String id; 5 6 public Student(String name, int age, String id) { 7 this.name = name; 8 this.age = age; 9 this.id = id; 10 } 11 12 // getter 和 setter 方法 13 14 @Override 15 public Student clone() throws CloneNotSupportedException { 16 return (Student) super.clone(); 17 } 18 }
在这个例子中,我们实现了Cloneable接口并重写了clone()方法,这样我们就可以通过调用clone()方法来创建Student对象的副本。例如,我们可以通过以下方式来创建一个新的Student对象:
Student student1 = new Student("Alice", 20, "001");
Student student2 = student1.clone();
这里我们首先创建了一个Student对象student1,然后通过调用clone()方法来创建一个新的Student对象student2。由于Java中的clone()方法是浅拷贝,因此student2和student1会共享相同的name、age和id属性,但是它们是两个不同的对象。需要注意的是,使用Java中的clone()方法需要注意一些细节,例如对象的属性如果是引用类型,则需要实现深拷贝,否则修改一个对象的属性会影响到所有副本对象的属性。
解决的问题:原型模式提供了一种简单有效的方式来复制对象,避免了复杂的对象初始化过程,并提高了代码的灵活性和可扩展性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~