Java常见设计模式

设计模式(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)是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

组成部分

  1. ‌**Subject(主题)**‌:它知道它的观察者,并提供注册和删除观察者的接口。主题会维护一个观察者列表,并在其状态改变时通知所有观察者。

  2. ‌**Observer(观察者)**‌:是一个抽象类或接口,为所有的具体观察者定义一个更新接口,以便在得到主题的通知时更新自己。

  3. ‌**ConcreteSubject(具体主题)**‌:将有关状态存入具体观察者对象,并在状态发生改变时,向它的各个观察者发出通知。

  4. ‌**ConcreteObserver(具体观察者)**‌:实现Observer接口,以便在得到主题的通知时更新自身的状态。

实现步骤

  1. 定义Observer接口 ‌:通常包括一个update方法,当被观察对象的状态发生变化时,这个方法会被调用。

  2. 创建Subject类‌:包含观察者列表,以及注册、删除和通知观察者的方法。

  3. 创建具体观察者类 ‌:实现Observer接口,并在update方法中实现具体的更新逻辑。

  4. 创建具体主题类 ‌:继承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实例在接收到通知时都会更新自己的状态,这里是通过打印一条消息来实现的。

houXuBuChong

相关推荐
智慧老师12 分钟前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm14 分钟前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
V+zmm101341 小时前
基于微信小程序的乡村政务服务系统springboot+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
oneouto1 小时前
selenium学习笔记(二)
笔记·学习·selenium
sealaugh321 小时前
aws(学习笔记第十九课) 使用ECS和Fargate进行容器开发
笔记·学习·aws
Oneforlove_twoforjob1 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
xmh-sxh-13141 小时前
常用的缓存技术都有哪些
java
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter
J不A秃V头A2 小时前
IntelliJ IDEA中设置激活的profile
java·intellij-idea
DARLING Zero two♡2 小时前
【优选算法】Pointer-Slice:双指针的算法切片(下)
java·数据结构·c++·算法·leetcode