重生之设计模式:创建型模式的启示录
设计模式
设计模式(Design Pattern)是软件工程中的一种可复用解决方案,它描述了在特定情况下如何实现特定接口的一个或多个对象之间的职责。设计模式主要用于解决软件设计中常见的问题,这些问题在不同的软件项目中反复出现。设计模式不是可以直接转换为代码的具体实现,而是提供了一种模板或指导原则,帮助开发者在特定情况下做出合理的设计决策。
-
创建型模式(Creational Patterns):这些模式提供了创建对象的机制,同时隐藏创建逻辑,而不是直接使用new操作符实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。常见的创建型模式包括单例模式(Singleton)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)和原型模式(Prototype)。
-
结构型模式(Structural Patterns):这些模式涉及对象的组合。结构型模式识别出对象和类之间的关系,然后提供一种方式来组合对象和类以获得更大的结构。常见的结构型模式包括适配器模式(Adapter)、装饰器模式(Decorator)、代理模式(Proxy)、外观模式(Facade)和组合模式(Composite)。
-
行为型模式(Behavioral Patterns):这些模式特别关注对象之间的通信。行为型模式识别出对象间哪些通信是必要的,以及如何将这些通信分配给各个对象。常见的行为型模式包括策略模式(Strategy)、观察者模式(Observer)、状态模式(State)、责任链模式(Chain of Responsibility)和命令模式(Command)。
设计模式的目的是提高代码的可重用性、可读性和可维护性,同时降低软件系统的复杂性。通过应用设计模式,开发者可以避免重复发明轮子,并且可以利用社区中已经证明有效的解决方案。
单例模式(Singleton)
一种常用的软件设计模式,其核心目的是保证一个类在任何情况下都只有一个实例,并且提供一个全局访问点来获取这个唯一的实例。这种模式在很多场景下都非常有用,比如配置管理器、连接池、线程池、日志记录等,这些场合通常只需要一个实例就足够了。
单例模式的特点:
-
唯一性:单例类只能生成一个实例。
-
全局访问:单例类提供了一个全局访问点,可以访问其唯一的实例。
-
延迟初始化:单例实例在第一次被使用时才创建。
-
线程安全:在多线程环境下,单例类的实例化过程需要是线程安全的。
单例模式的实现方式:
1. 饿汉式(Eager Initialization)
饿汉式(Eager Initialization)单例模式是一种在类加载时就创建实例的方式。它的核心特点是“急切地创建实例”,即不管是否需要该实例,都会在类加载时进行实例化。下面是饿汉式单例模式的详细解释:
实现步骤
-
私有构造方法:首先,为了确保其他类不能直接实例化该类,我们将构造方法设置为私有。
-
私有静态实例变量:在类内部,创建一个私有静态变量,用于存储单例实例。
-
立即实例化:在类加载时,直接实例化该变量,确保实例在类加载后立即存在。
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton(); // 私有静态实例变量
private EagerSingleton() {
} // 私有构造方法
public static EagerSingleton getInstance() {
return instance;
}
}
在这个示例中,instance
是一个静态变量,属于 EagerSingleton
类,并在类加载时初始化。
优点
-
线程安全:在类加载时就创建了实例,不存在多线程并发访问的问题。
-
简单明了:实现简单,没有复杂的线程同步机制。
缺点
-
资源浪费:如果实例在整个程序运行过程中没有被使用,会造成资源的浪费。
-
不支持延迟加载:无法实现懒加载,即使实例不被使用,也会被创建。
总结
饿汉式单例模式适合对资源消耗不高的情况,或者单例对象必然会被使用的场景。这种模式在处理全局配置、数据库连接池、日志记录器等场景中非常有用。它通过在类加载时就创建实例,确保了线程安全性,但可能会造成不必要的资源占用,特别是当单例实例最终未被使用时
2.懒汉式(Lazy Initialization)
单例模式是一种在需要时才创建实例的单例实现方式。这种方式的优点是延迟加载,节省了内存资源,但缺点是初始化过程可能会稍微慢一些,因为需要在首次请求时创建实例。以下是懒汉式单例模式的详细解释:
特点
- 延迟加载:单例实例在第一次被请求时才创建,这可以节省资源,特别是当单例对象较大或者创建成本较高时。
- 线程安全:为了保证在多线程环境下只创建一个实例,需要采取额外的同步措施。
实现方式
懒汉式单例模式有几种不同的实现方式,包括非线程安全版本、同步方法实现、同步代码块实现、双重检查锁定(Double-Checked Locking)和静态内部类实现。
1. 非线程安全版本
这是最基本的懒汉式实现,但它在多线程环境下不是线程安全的。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
2. 同步方法实现
通过将`getInstance`方法声明为`synchronized`来保证线程安全,但这种方式会影响性能,因为每次调用都会进行同步。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
3. 同步代码块实现
只在实例化对象时进行同步,以减少同步的开销。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
4. 双重检查锁定(Double-Checked Locking)
这种方式结合了延迟加载和线程安全,通过双重检查来减少不必要的同步。
public class LazySingleton {
private static volatile LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
5. 静态内部类
利用Java的类加载机制来实现线程安全的懒汉式单例。
public class LazySingleton {
private LazySingleton() {}
private static class Holder {
private static final LazySingleton INSTANCE = new LazySingleton();
}
public static LazySingleton getInstance() {
return Holder.INSTANCE;
}
}
优缺点
- 优点:资源利用率高,只有在需要时才创建实例;可以控制实例化的时机。
- 缺点:在多线程环境下需要额外处理线程安全问题,可能会影响性能。
懒汉式单例模式适用于实例化成本较高或者不确定是否会使用到单例对象的场景。通过不同的实现方式,可以在保证线程安全的同时,实现延迟加载,从而提高资源的利用效率。
枚举(Enum)
是Java语言中一种特殊的类,它用于表示一组常量。在Java中,枚举类型非常强大,不仅可以用来定义常量,还可以拥有字段、方法,甚至实现接口。枚举类型在单例模式中的应用是一种简洁且线程安全的方法,因为它天生就是单例的。
枚举类型的基本语法
public enum EnumName {
CONSTANT1,
CONSTANT2,
CONSTANT3;
// 可以添加字段和方法
public void doSomething() {
// 实现代码
}
}
枚举实现单例模式
使用枚举实现单例模式非常简单,只需要定义一个包含单个元素的枚举类型即可。这个枚举元素就是单例实例。
public enum Singleton {
INSTANCE;
public void doSomething() {
// 实现代码
}
}
在这个例子中,`Singleton`枚举类型只有一个实例`INSTANCE`,这个实例在枚举类型被加载时就已经被创建。由于枚举类型的初始化是线程安全的,因此不需要额外的同步措施来保证单例的唯一性。
枚举单例的特点
1. 简洁性:使用枚举实现单例非常简单,代码量少。
2. 线程安全:枚举类型的初始化是线程安全的,不需要额外的同步措施。
3. 序列化安全:枚举类型天生支持序列化机制,不需要额外的处理就能防止反序列化创建新的对象。
4. 增强的安全性:由于枚举类型的所有元素都是在编译时创建的,因此无法通过反射机制来调用构造方法创建额外的实例。
获取单例实例
获取枚举单例实例非常简单,只需要通过枚举类型直接访问即可:
Singleton singletonInstance = Singleton.INSTANCE;
总结
枚举类型是实现单例模式的一种非常有效的方式,它不仅代码简洁,而且天然具有线程安全和序列化安全的特点。因此,在需要实现单例模式时,推荐使用枚举类型。
未完待续....................