设计模式(Design Patterns)是软件工程中用于解决特定问题的一系列最佳实践。它们是经过时间考验的、被广泛认可的软件设计经验,可以帮助开发者在面对常见问题时做出更好的设计决策。设计模式不是现成的代码,而是一套指导原则,用来指导开发者如何组织代码结构,以便于更好地应对变化和提高代码的可维护性。
设计模式三大类
**创建型模式(Creational Patterns)**
主要关注对象的创建,隐藏对象创建的复杂性,提高程序的可扩展性。常见的创建型模式包括单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。
**结构型模式(Structural Patterns)**
主要关注对象的组合,通过对象的组合提高程序的模块化。常见的结构型模式包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。
**行为型模式(Behavioral Patterns)**
主要关注对象之间的交互,描述对象如何协作以完成某种任务。常见的行为型模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式和访问者模式。
单例模式(Singleton Pattern)
单例模式是一种常用的软件设计模式,用于确保一个类仅有一个实例,并提供一个全局访问点。在Java中,实现单例模式通常需要考虑线程安全和延迟加载(懒汉式)或立即加载(饿汉式)等因素。
饿汉式(线程安全)
饿汉式单例模式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。由于是在静态代码块中初始化的,所以它是线程安全的。
java
public class SingletonEager {
// 在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
private static final SingletonEager INSTANCE = new SingletonEager();
// 私有构造函数,防止外部通过new创建实例
private SingletonEager() {}
// 提供一个全局的静态方法,返回唯一实例
public static SingletonEager getInstance() {
return INSTANCE;
}
}
懒汉式(线程不安全)
懒汉式单例模式在第一次使用时才进行初始化,实现了延迟加载。但是,如果多个线程同时访问getInstance()
方法,并且此时实例还未被创建,那么就有可能导致多个实例被创建,从而违反了单例模式的原则。
java
public class SingletonLazyUnsafe {
// 注意,这里不是final,也不是static
private static SingletonLazyUnsafe instance;
// 私有构造函数,防止外部通过new创建实例
private SingletonLazyUnsafe() {}
// 提供一个全局的静态方法,返回唯一实例
public static SingletonLazyUnsafe getInstance() {
if (instance == null) {
instance = new SingletonLazyUnsafe();
}
return instance;
}
// 注意:上面的实现是线程不安全的
}
懒汉式(线程安全,但效率低)
为了解决懒汉式单例模式的线程安全问题,可以在getInstance()
方法上加上synchronized
关键字,但这会导致效率低下,因为每次调用getInstance()
方法时都会进行线程锁定判断。
java
public class SingletonLazySafeButInefficient {
private static SingletonLazySafeButInefficient instance;
private SingletonLazySafeButInefficient() {}
// 使用synchronized关键字保证线程安全
public static synchronized SingletonLazySafeButInefficient getInstance() {
if (instance == null) {
instance = new SingletonLazySafeButInefficient();
}
return instance;
}
// 注意:这种方法虽然线程安全,但效率低下
}
双重检查锁定(Double-Checked Locking)
双重检查锁定是一种更加高效的实现懒汉式单例模式的方法。它首先检查实例是否存在,如果不存在才进行同步,从而减少了同步的开销。但是,需要注意的是,双重检查锁定必须配合volatile
关键字使用,以确保实例创建的可见性。
java
public class SingletonDoubleChecked {
// 使用volatile关键字保证多线程环境下instance变量的可见性和禁止指令重排序
private static volatile SingletonDoubleChecked instance;
private SingletonDoubleChecked() {}
public static SingletonDoubleChecked getInstance() {
if (instance == null) {
// 第一次检查
synchronized (SingletonDoubleChecked.class) {
// 第二次检查
if (instance == null) {
instance = new SingletonDoubleChecked();
}
}
}
return instance;
}
}
工厂模式
在Java中,实现单例模式时需要注意线程安全和性能之间的平衡。通常,推荐使用饿汉式单例模式或双重检查锁定的懒汉式单例模式。
在Java中,工厂模式是一种常用的设计模式,它主要用于创建对象,而无需指定将要创建的具体类。这种模式通过定义一个共同的接口来创建对象,但让子类决定要实例化的类是哪一个。工厂模式隐藏了创建逻辑,并且可以在不修改客户端代码的情况下引入新的具体产品类。
工厂模式主要分为三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。
1. 简单工厂模式
简单工厂模式不是GoF(四人帮)设计模式中的一种,但它经常被作为学习工厂模式的起点。在这种模式下,我们有一个工厂类,它根据传入的参数来决定创建哪个类的实例。
java
interface Product {
void use();
}
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductA");
}
}
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductB");
}
}
// 简单工厂类
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;
}
}
// 客户端代码
public class FactoryPatternDemo {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.use();
Product productB = SimpleFactory.createProduct("B");
productB.use();
}
}
2. 工厂方法模式
工厂方法模式将对象的创建延迟到其子类中进行。在工厂方法模式中,我们定义了一个创建对象的接口,但让子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类中进行。
java
interface Product {
void use();
}
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductA");
}
}
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductB");
}
}
interface Creator {
Product factoryMethod();
}
class ConcreteCreatorA implements Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}
class ConcreteCreatorB implements Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}
// 客户端代码
public class FactoryMethodPatternDemo {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
Product productA = creatorA.factoryMethod();
productA.use();
Creator creatorB = new ConcreteCreatorB();
Product productB = creatorB.factoryMethod();
productB.use();
}
}
3. 抽象工厂模式
抽象工厂模式提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。与工厂方法模式相比,抽象工厂模式创建的是产品族,而不是单个产品。
java
interface AbstractProductA {
void use();
}
interface AbstractProductB {
void use();
}
class ProductA1 implements AbstractProductA {
@Override
public void use() {
System.out.println("Using ProductA1");
}
}
class ProductA2 implements AbstractProductA {
@Override
public void use() {
System.out.println("Using ProductA2");
}
}
class ProductB1 implements AbstractProductB {
@Override
public void use() {
System.out.println("Using ProductB1");
}
}
class ProductB2 implements AbstractProductB {
@Override
public void use() {
System.out.println("Using ProductB2");
}
}
interface AbstractFactory {
AbstractProductA createProductA();
AbstractProductB createProductB();
}
class ConcreteFactory1 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
return new ProductA1();
}
@Override
public AbstractProductB createProductB() {
return new ProductB
}
}
代理模式
代理模式(Proxy Pattern)在Java中是一种常用的结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通过创建一个代理对象,在客户端和目标对象之间起到中介的作用,从而实现对目标对象的间接访问和操作。这种方式可以增加额外的功能,如权限控制、日志记录、事务处理等,同时又不修改目标对象的代码。
在Java中实现代理模式,主要有两种方式:静态代理和动态代理。
1. 静态代理
静态代理是在编译时就确定代理类,代理类和目标类实现了相同的接口,并在代理类中通过调用目标类的方法来增强原有功能。
java
// 接口
interface Image {
void display();
}
// 目标类
class RealImage implements Image {
@Override
public void display() {
System.out.println("Displaying Real Image");
}
}
// 代理类
class ProxyImage implements Image {
private RealImage realImage;
public ProxyImage(RealImage realImage) {
this.realImage = realImage;
}
@Override
public void display() {
// 在调用真实对象的方法前后添加额外操作
prepareImage();
realImage.display();
unloadImage();
}
private void prepareImage() {
System.out.println("Preparing Image");
}
private void unloadImage() {
System.out.println("Unloading Image");
}
}
// 客户端代码
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage(new RealImage());
image.display();
}
}
2. 动态代理
动态代理是在运行时动态生成代理类,并创建代理对象。Java的动态代理主要利用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来实现。
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Image {
void display();
}
class RealImage implements Image {
@Override
public void display() {
System.out.println("Displaying Real Image");
}
}
// 代理的InvocationHandler实现
class ImageInvocationHandler implements InvocationHandler {
private Object target;
public ImageInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在方法调用之前可以添加额外处理
System.out.println("Before method: " + method.getName());
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 在方法调用之后可以添加额外处理
System.out.println("After method: " + method.getName());
return result;
}
}
// 客户端代码
public class DynamicProxyPatternDemo {
public static void main(String[] args) {
Image realImage = new RealImage();
InvocationHandler handler = new ImageInvocationHandler(realImage);
// 创建代理对象
Image proxyImage = (Image) Proxy.newProxyInstance(
realImage.getClass().getClassLoader(),
realImage.getClass().getInterfaces(),
handler);
// 调用代理对象的方法
proxyImage.display();
}
}
在动态代理中,代理类的字节码是在运行时动态生成的,并且代理对象是在运行时创建的。这种方式提供了更大的灵活性,因为你可以在不修改目标类代码的情况下,通过实现InvocationHandler
接口来添加新的功能。
观察者模式
在Java中,观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
组成部分
-
**Subject(主题)**:它知道它的观察者,并提供注册和删除观察者的接口。主题会维护一个观察者列表,并在其状态改变时通知所有观察者。
-
**Observer(观察者)**:是一个抽象类或接口,为所有的具体观察者定义一个更新接口,以便在得到主题的通知时更新自己。
-
**ConcreteSubject(具体主题)**:将有关状态存入具体观察者对象,并在状态发生改变时,向它的各个观察者发出通知。
-
**ConcreteObserver(具体观察者)**:实现Observer接口,以便在得到主题的通知时更新自身的状态。
实现步骤
-
定义Observer接口 :通常包括一个
update
方法,当被观察对象的状态发生变化时,这个方法会被调用。 -
创建Subject类:包含观察者列表,以及注册、删除和通知观察者的方法。
-
创建具体观察者类 :实现Observer接口,并在
update
方法中实现具体的更新逻辑。 -
创建具体主题类 :继承Subject类,并在需要通知观察者时调用
notifyObservers
方法。
示例代码
java
import java.util.ArrayList;
import java.util.List;
// Observer接口
interface Observer {
void update(String message);
}
// Subject类
class Subject {
private List<Observer> observers = new ArrayList<>();
void registerObserver(Observer o) {
observers.add(o);
}
void removeObserver(Observer o) {
observers.remove(o);
}
void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
// ConcreteObserver类
class ConcreteObserver implements Observer {
private String name;
ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received: " + message);
}
}
// 使用示例
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
Observer observer1 = new ConcreteObserver("Observer1");
Observer observer2 = new ConcreteObserver("Observer2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.notifyObservers("Hello Observers!");
subject.removeObserver(observer1);
subject.notifyObservers("Hello again, but Observer1 is gone!");
}
}
在上面的示例中,Subject
类维护了一个观察者列表,并提供方法来注册和删除观察者。当Subject
的状态发生变化时(在这个例子中是通过调用notifyObservers
方法模拟的),它会通知所有已注册的观察者。每个ConcreteObserver
实例在接收到通知时都会更新自己的状态,这里是通过打印一条消息来实现的。