一、引言
在 Java 开发的世界里,设计模式就像是一把把神奇的钥匙,能够帮助我们解决各种复杂的问题,让代码更加优雅、可维护和可扩展。今天咱们就来聊聊三种非常实用的设计模式:单例模式、工厂模式和观察者模式。这三种模式在实际开发中应用广泛,理解并掌握它们,能让你的 Java 编程水平更上一层楼。
二、单例模式
2.1 应用场景
单例模式就像是一个独一无二的“管家”,在整个系统中,它只允许创建一个实例。这种模式在很多场景下都非常有用,比如数据库连接池、日志记录器、线程池等。这些组件在系统中只需要一个实例就够了,如果创建多个实例,可能会导致资源浪费或者数据不一致的问题。
2.2 实现方式
2.2.1 饿汉式单例
// 饿汉式单例,在类加载时就创建实例
public class EagerSingleton {
// 私有静态成员变量,存储单例实例
private static final EagerSingleton INSTANCE = new EagerSingleton();
// 私有构造方法,防止外部创建实例
private EagerSingleton() {}
// 公共静态方法,获取单例实例
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
饿汉式单例的优点是实现简单,线程安全。因为在类加载时就创建了实例,所以不会出现多个线程同时创建实例的问题。缺点是如果这个单例实例在系统中一直没有被使用,会造成内存的浪费。
2.2.2 懒汉式单例
// 懒汉式单例,在第一次使用时创建实例
public class LazySingleton {
// 私有静态成员变量,存储单例实例
private static LazySingleton instance;
// 私有构造方法,防止外部创建实例
private LazySingleton() {}
// 公共静态方法,获取单例实例
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
懒汉式单例的优点是在第一次使用时才创建实例,避免了内存的浪费。缺点是使用了synchronized关键字,会影响性能,因为每次调用getInstance()方法都会进行同步操作。
2.2.3 双重检查锁定单例
// 双重检查锁定单例,结合了懒汉式和线程安全
public class DoubleCheckedLockingSingleton {
// 私有静态成员变量,使用 volatile 关键字保证可见性
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;
}
}
双重检查锁定单例结合了懒汉式和线程安全的优点,既避免了内存的浪费,又保证了线程安全。使用volatile关键字可以防止指令重排序,确保在多线程环境下不会出现问题。
2.3 技术优缺点
优点:
- 节省系统资源,避免重复创建实例。
- 保证一个类只有一个实例,便于对实例进行控制和管理。
缺点:
- 可能会造成内存泄漏,如果单例实例持有大量的资源,并且在系统中一直不释放。
- 违反了单一职责原则,单例类既要负责自身的创建,又要负责业务逻辑。
2.4 注意事项
- 在多线程环境下,要确保单例的创建是线程安全的。
- 避免在单例类中使用静态变量,因为静态变量会在类加载时就被初始化,可能会导致单例模式失效。
三、工厂模式
3.1 应用场景
工厂模式就像是一个“工厂老板”,负责创建对象。当创建对象的逻辑比较复杂,或者需要根据不同的条件创建不同类型的对象时,使用工厂模式可以将对象的创建和使用分离,提高代码的可维护性和可扩展性。比如在游戏开发中,根据不同的关卡创建不同的怪物;在电商系统中,根据不同的支付方式创建不同的支付对象。
3.2 实现方式
3.2.1 简单工厂模式
// 定义一个产品接口
interface Product {
void use();
}
// 具体产品类 A
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品 A");
}
}
// 具体产品类 B
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品 B");
}
}
// 简单工厂类
class SimpleFactory {
// 根据类型创建产品
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
}
return null;
}
}
简单工厂模式的优点是实现简单,使用方便。缺点是工厂类的职责过重,如果需要添加新的产品,需要修改工厂类的代码,违反了开闭原则。
3.2.2 工厂方法模式
// 定义一个产品接口
interface Product {
void use();
}
// 具体产品类 A
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品 A");
}
}
// 具体产品类 B
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品 B");
}
}
// 定义一个工厂接口
interface Factory {
Product createProduct();
}
// 具体工厂类 A,负责创建产品 A
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类 B,负责创建产品 B
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
工厂方法模式将对象的创建延迟到具体的工厂子类中,每个具体的工厂类负责创建一种具体的产品。优点是符合开闭原则,如果需要添加新的产品,只需要创建一个新的具体工厂类即可,不需要修改现有的代码。缺点是工厂子类过多,会导致代码结构变得复杂。
3.3 技术优缺点
优点:
- 降低了代码的耦合度,将对象的创建和使用分离。
- 提高了代码的可维护性和可扩展性,符合开闭原则。
缺点:
- 增加了代码的复杂度,需要创建多个工厂类。
- 工厂类的职责可能会过重,尤其是在简单工厂模式中。
3.4 注意事项
- 合理设计工厂类的职责,避免工厂类的职责过重。
- 在使用工厂方法模式时,要注意工厂子类的管理,避免子类过多导致代码结构混乱。
四、观察者模式
4.1 应用场景
观察者模式就像是一场“发布会”,有一个“发布者”(被观察对象)和多个“观察者”。当发布者的状态发生变化时,会通知所有的观察者,让它们做出相应的处理。这种模式在很多场景下都非常有用,比如股票行情系统,当股票价格发生变化时,会通知所有的股民;在消息推送系统中,当有新的消息时,会通知所有的订阅者。
4.2 实现方式
import java.util.ArrayList;
import java.util.List;
// 定义一个观察者接口
interface Observer {
void update(String message);
}
// 定义一个被观察对象类
class Subject {
// 存储观察者的列表
private List<Observer> observers = new ArrayList<>();
// 存储状态信息
private String state;
// 注册观察者
public void attach(Observer observer) {
observers.add(observer);
}
// 移除观察者
public void detach(Observer observer) {
observers.remove(observer);
}
// 通知所有观察者
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
// 设置状态信息
public void setState(String state) {
this.state = state;
notifyObservers();
}
}
// 具体观察者类
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到消息:" + message);
}
}
4.3 技术优缺点
优点:
- 实现了对象之间的松耦合,被观察对象和观察者之间不需要直接依赖。
- 支持广播通信,当被观察对象的状态发生变化时,会通知所有的观察者。
缺点:
- 如果观察者过多,通知所有观察者的时间会变长,影响系统的性能。
- 观察者和被观察对象之间可能会出现循环依赖的问题。
4.4 注意事项
- 避免观察者和被观察对象之间的循环依赖,否则会导致系统出现死循环。
- 在通知观察者时,要考虑性能问题,可以采用异步通知的方式。
五、文章总结
单例模式、工厂模式和观察者模式是 Java 开发中非常实用的设计模式。单例模式确保一个类只有一个实例,节省系统资源;工厂模式将对象的创建和使用分离,提高代码的可维护性和可扩展性;观察者模式实现了对象之间的松耦合,支持广播通信。
在实际开发中,我们要根据具体的应用场景选择合适的设计模式。同时,要注意设计模式的优缺点和注意事项,合理使用设计模式,才能让我们的代码更加健壮、高效。
评论