Java 原型模式、建造者模式、单例模式
原型模式、建造者模式
原型模式
原型模式(Prototype Pattern)是一种创建型设计模式,它允许你通过复制现有对象来创建新对象,而不是通过实例化类。这种模式在需要大量相似对象时非常有用,因为它可以减少创建对象的开销。
原型模式的應用場景
- 性能和资源管理:当创建对象的代价较大时,可以使用原型模式减少创建对象的开销。
- 避免构造函数的复杂性:如果对象的创建过程非常复杂,可以通过克隆已有对象来简化创建过程。
- 隔离复杂对象的创建:在某些情况下,直接使用构造函数可能会使代码变得复杂,而通过克隆可以简化代码。
- 缓存实例:在某些场景下,可以预先创建一些对象并缓存起来,需要时直接克隆这些对象。
Java 原型模式的代碼實例
以下是一个简单的Java示例,展示了如何使用原型模式:
// 定义一个抽象的原型接口
interface Prototype {
Prototype clone();
}
// 实现具体的原型类
class ConcretePrototype implements Prototype {
private String field;
public ConcretePrototype(String field) {
this.field = field;
}
@Override
public Prototype clone() {
return new ConcretePrototype(this.field);
}
@Override
public String toString() {
return "ConcretePrototype{" +
"field='" + field + '\'' +
'}';
}
}
// 客户端代码
public class PrototypePatternDemo {
public static void main(String[] args) {
// 创建一个原始对象
ConcretePrototype original = new ConcretePrototype("Original");
System.out.println("Original: " + original);
// 克隆原始对象
ConcretePrototype clone = (ConcretePrototype) original.clone();
System.out.println("Clone: " + clone);
}
}
在这个示例中:
Prototype
接口定义了一个clone
方法,用于克隆对象。ConcretePrototype
类实现了Prototype
接口,并提供了clone
方法的具体实现。- 在客户端代码中,我们创建了一个原始对象,并通过调用
clone
方法创建了它的副本。
这样,通过原型模式,我们可以方便地创建对象的副本,而不需要每次都通过构造函数来创建新的对象。
建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,它允许你通过一步一步地构建复杂对象。这种模式特别适用于需要创建具有多个可选参数的对象时,可以避免构造函数参数过多的问题。
建造者模式的應用場景
- 复杂对象的创建:当一个对象有很多属性,并且这些属性可能有不同的组合时,使用建造者模式可以简化对象的创建过程。
- 避免构造函数参数过多:如果一个类的构造函数有多个参数,使用建造者模式可以使代码更加清晰和易于维护。
- 不可变对象:在创建不可变对象时,建造者模式可以帮助确保对象在构建过程中不会被修改。
- 流式接口:建造者模式通常提供流式接口,使代码更具可读性。
Java 建造者模式的代碼實例
以下是一个简单的Java示例,展示了如何使用建造者模式:
// 产品类
class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) { this.partA = partA; }
public void setPartB(String partB) { this.partB = partB; }
public void setPartC(String partC) { this.partC = partC; }
@Override
public String toString() {
return "Product{" +
"partA='" + partA + '\'' +
", partB='" + partB + '\'' +
", partC='" + partC + '\'' +
'}';
}
}
// 抽象建造者类
abstract class Builder {
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
public Product getResult() {
return product;
}
}
// 具体建造者类
class ConcreteBuilder extends Builder {
@Override
public void buildPartA() {
product.setPartA("Part A");
}
@Override
public void buildPartB() {
product.setPartB("Part B");
}
@Override
public void buildPartC() {
product.setPartC("Part C");
}
}
// 指挥者类
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
}
}
// 客户端代码
public class BuilderPatternDemo {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct();
Product product = builder.getResult();
System.out.println(product);
}
}
在这个示例中:
Product
类是我们要构建的复杂对象。Builder
是一个抽象类,定义了构建不同部分的方法。ConcreteBuilder
是具体的建造者类,实现了Builder
类的方法。Director
类负责控制建造过程,按照一定的顺序调用建造者的方法。- 在客户端代码中,我们创建了一个具体的建造者实例和一个指挥者实例,然后通过指挥者来构建产品。
这样,通过建造者模式,我们可以灵活地构建复杂对象,同时保持代码的清晰和可维护性。
单例模式
单例模式(Singleton Pattern)是Java设计模式中最简单的一种,它确保一个类在整个应用程序运行期间只有一个实例,并提供一个全局访问点。这种模式在需要控制资源唯一性或对共享资源进行管理时非常有用。
单例模式是一种创建型设计模式,其目的是确保某个类在系统中只有一个实例存在,并提供一个全局访问该实例的方法。通过这种方式,可以有效地控制资源的使用,避免不必要的实例化开销。单例模式适用于以下场景:
- 需要控制资源的唯一性,如数据库连接池、配置文件管理器等。
- 需要对共享资源进行集中管理,如线程池、日志记录器等。
二、单例模式的实现方式
单例模式有多种实现方法,主要包括懒汉式、饿汉式、双重检查锁和静态内部类。每种方法都有其优缺点,适用于不同的场景。
1. 懒汉式(Lazy Initialization)
懒汉式单例模式只有在第一次使用时才会创建实例。这种方式适用于实例化开销较大的对象,且该对象在程序运行初期不一定会被使用。
代码示例:
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
// 私有化构造函数
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
适用场景:
- 实例化开销较大的对象。
- 程序启动时不需要立即加载的对象。
优缺点:
- 优点:延迟加载,减少内存开销。
- 缺点:多线程环境下性能较差,因为每次获取实例时都需要进行同步。
2. 饿汉式(Eager Initialization)
饿汉式单例模式在类加载时就创建实例,适用于程序运行过程中必然会使用到的实例。
代码示例:
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
// 私有化构造函数
}
public static EagerSingleton getInstance() {
return instance;
}
}
适用场景:
- 启动时需要立即加载并长期使用的对象。
优缺点:
- 优点:实现简单,线程安全。
- 缺点:由于实例是在类加载时创建的,可能会导致内存浪费,尤其是在实例一直没有被使用的情况下。
3. 双重检查锁(Double-Check Locking)
双重检查锁机制在多线程环境下使用,确保实例的唯一性和线程安全性。
代码示例:
public class DoubleCheckedLockingSingleton {
private static volatile DoubleCheckedLockingSingleton instance;
private DoubleCheckedLockingSingleton() {
// 私有化构造函数
}
public static DoubleCheckedLockingSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedLockingSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedLockingSingleton();
}
}
}
return instance;
}
}
适用场景:
- 需要延迟加载单例对象且需要确保多线程安全的场景。
优缺点:
- 优点:线程安全,避免了不必要的同步开销。
- 缺点:实现复杂,可能会增加代码的可维护性难度。
4. 静态内部类(Static Inner Class)
利用Java的类加载机制,静态内部类实现单例模式既实现了延迟加载,又保证了线程安全。
代码示例:
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
// 私有化构造函数
}
private static class SingletonHelper {
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
适用场景:
- 需要延迟加载但不希望增加代码复杂度的场景。
优缺点:
- 优点:延迟加载,线程安全,且实现简单。
- 缺点:无法在实例化时传递参数。
三、单例模式的应用场景
单例模式在实际应用中非常广泛,特别是在需要控制资源唯一性的场合。以下是几个典型的应用场景:
1. 配置管理器
在一个电商系统中,各种配置如数据库连接、API密钥等通常都是全局的且不会频繁更改。这些配置数据可以封装在一个配置管理器类中,并使用单例模式来确保只有一个实例来管理所有配置。
代码示例:
public class ConfigurationManager {
private static ConfigurationManager instance;
private Properties properties;
private ConfigurationManager() {
properties = new Properties();
// 加载配置文件
try (InputStream input = new FileInputStream("config.properties")) {
properties.load(input);
} catch (IOException e) {
e.printStackTrace();
}
}
public static synchronized ConfigurationManager getInstance() {
if (instance == null) {
instance = new ConfigurationManager();
}
return instance;
}
// 获取配置信息的方法
public String getProperty(String key) {
return properties.getProperty(key);
}
}
在这个例子中,ConfigurationManager
类使用了懒汉式单例模式,确保配置文件只被加载一次,并且在应用程序的任何地方都可以通过ConfigurationManager.getInstance()
来获取配置实例。
2. 数据库连接池
数据库连接池是用来管理和复用数据库连接的对象。为了避免频繁创建和销毁连接带来的性能开销,可以使用单例模式来管理连接池。
代码示例:
public class DatabaseConnectionPool {
private static DatabaseConnectionPool instance;
private List<Connection> connections;
private DatabaseConnectionPool() {
connections = new ArrayList<>();
// 初始化连接池中的连接
for (int i = 0; i < 10; i++) {
connections.add(createConnection());
}
}
public static synchronized DatabaseConnectionPool getInstance() {
if (instance == null) {
instance = new DatabaseConnectionPool();
}
return instance;
}
// 获取连接的方法
public Connection getConnection() {
if (connections.isEmpty()) {
throw new SQLException("No available connections");
} else {
return connections.remove(connections.size() - 1);
}
}
// 释放连接的方法
public void releaseConnection(Connection connection) {
connections.add(connection);
}
}
在这个例子中,DatabaseConnectionPool
类使用了懒汉式单例模式,确保连接池只被初始化一次,并且在应用程序的任何地方都可以通过DatabaseConnectionPool.getInstance()
来获取连接池实例。
3. 日志记录器
在大型应用中,日志记录通常由一个集中的日志记录器负责。通过单例模式,可以确保日志记录器的唯一性,避免多个实例导致的日志混乱。
代码示例:
public class Logger {
private static Logger instance;
private static List<String> logEntries;
private Logger() { }
public static synchronized Logger getInstance() {
if (instance == null) {
instance = new Logger();
logEntries = new ArrayList<>();
}
return instance;
}
// 记录日志的方法
public void log(String message) {
logEntries.add(message);
System.out.println(message); // 输出到控制台,也可以输出到文件或其他介质
}
}
在这个例子中,Logger
类使用了懒汉式单例模式,确保日志记录器只被初始化一次,并且在应用程序的任何地方都可以通过Logger.getInstance()
来获取日志记录器实例。
END