最常用的7种设计模式

文摘   2025-01-27 08:01   新疆  

Spring Boot 3实战案例合集》现已囊括超过90篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最前沿的技术资讯与实践经验。欢迎积极订阅,享受不断升级的知识盛宴!订阅用户将特别获赠合集内所有文章的最终版MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。

【重磅发布】《Spring Boot 3实战案例锦集》PDF电子书现已出炉!

🎉🎉我们精心打造的《Spring Boot 3实战案例锦集》PDF电子书现已正式完成,目前已经有90个案例,后续还将继续更新。文末有电子书目录。

💪💪永久更新承诺

我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。这意味着,随着技术的不断发展和Spring Boot 3的深入应用,我们的电子书也将持续更新,确保您始终掌握最前沿、最实用的技术知识。

💌💌如何获取
请立即订阅我们的合集点我订阅,并通过私信联系我们,我们将第一时间将电子书发送给您。

现在就订阅合集




环境:Java21



1. 简介

设计模式它提供了一套经过验证的、可复用的设计思路,用于解决在特定上下文中反复出现的问题。这些模式涵盖了创建型、结构型和行为型三大类别,旨在提高代码的可重用性、可读性和可扩展性。通过应用设计模式,开发人员可以更加高效地设计出灵活、易于维护和扩展的软件系统。

在我们的日常开发中,设计模式无处不在。本篇文章,我将结合实际工作场景和源代码示例,详细的讲述工作中最常用的7种设计模式。

2. 七种设计模式

2.1 单例模式

单例模式是一种创建型设计模式,确保一个类仅有一个实例,并且它通常用于管理共享资源,如配置、缓存、线程池等。

下面介绍2种单例模式的实现

双重检查DCL

public class Singleton {  private static volatile Singleton instance;
private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}

解释:

private static volatile Singleton instance


volatile关键字确保了实例变量的可见性。当一个线程修改instance的值时,其他线程可以立即看到最新的值。同时,volatile还防止了指令重排序。在创建单例实例时,new Singleton()操作不是原子操作,它包括三个步骤:分配内存空间、初始化对象以及将对象引用赋值给instance。如果没有volatile,可能会发生指令重排序,导致其他线程获取到尚未完全初始化的实例对象。

private Singleton()

私有构造函数确保了Singleton类不能从外部直接实例化,从而保证了单例模式的唯一性。

public static Singleton getInstance()方法体

首先,它检查instance是否为null。如果不为null,则直接返回实例,避免进入同步代码块,从而提高性能。如果instance为null,则进入同步代码块。

synchronized (Singleton.class)语句锁定了Singleton类,确保同时只有一个线程可以进入创建实例的代码块。它再次检查instance是否为null。这是为了防止多个线程同时通过第一次检查,进入同步代码块,并重复创建实例。当instance为null时,执行instance = new Singleton()来创建单例实例。

枚举实现

在《Effective Java》中更推荐这种实现单例模式的方式。

public enum SingletonEnum {  INSTANCE;
public void operator() { System.out.println("TODO PACK"); }}

解释:

在Java中,枚举类型(enum)是一种特殊的类,它保证了实例的唯一性,并且天生就是线程安全的。

INSTANCE是SingletonEnum枚举的唯一实例。你可以通过SingletonEnum.INSTANCE来获取这个单例。

你可以在枚举中添加自定义方法,比如operator(),来实现单例需要完成的业务逻辑。

枚举单例可以防止反射和序列化破坏单例模式,因为Java规范保证了枚举类型的实例化是唯一的,在序列化和反序列化过程中不会创建新的实例。

JDK中的应用:

  • java.lang.Runtime.getRuntime()

  • java.util.logging.Logger

     

Spring中的应用:

默认情况下,Spring中定义的Bean都是单例的;可以通过@Scope("prototype")修改为多例。

2.2 工厂模式

工厂模式是一种创建型设计模式,它提供了一种无需指定具体类的情况下创建对象的机制。在工厂模式中,创建对象的代码被封装在一个工厂类中,客户端通过调用工厂类的方法来获取所需的对象,而无需直接实例化对象。这种方式使得代码更加灵活和可扩展,因为如果需要创建不同类型的对象,只需修改工厂类的实现即可,而无需修改客户端代码。

简单工厂实现

public class FactoryPatternExample {  static abstract class Vehicle {    public abstract void drive();  }  static class Car extends Vehicle {    @Override    public void drive() {      System.out.println("Driving a car.");    }  }  static class Bicycle extends Vehicle {    @Override    public void drive() {      System.out.println("Riding a bicycle.");    }  }
static class Truck extends Vehicle { @Override public void drive() { System.out.println("Driving a truck."); }  } class VehicleFactory { public static Vehicle createVehicle(String vehicleType) { return switch (vehicleType) { case "car" -> new Car(); case "bicycle" -> new Bicycle(); case "truck" -> new Truck(); default -> throw new IllegalArgumentException("无效参数类型: " + vehicleType); } ; }  } public static void main(String[] args) { Vehicle car = VehicleFactory.createVehicle("car"); car.drive();
Vehicle bicycle = VehicleFactory.createVehicle("bicycle"); bicycle.drive();
Vehicle truck = VehicleFactory.createVehicle("truck"); truck.drive(); }}

解释:

当你需要制造一辆车时,不是直接调用Car、Bicycle或Truck的构造函数,而是调用VehicleFactory.createVehicle()方法,并传入你想要制造的车辆类型。工厂会根据你提供的类型创建并返回相应的车辆实例。你可以通过调用drive()方法来使用制造出的车辆。

优化,如果你不想使用如上switch方式来创建具体的实例,那么你可以通过反射机制:

class VehicleFactory2 {  public static Vehicle createVehicle(Class<? extends Vehicle> vehicleClass) {    try {      Constructor<? extends Vehicle> constructor = vehicleClass.getDeclaredConstructor();      return constructor.newInstance();    } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {      throw new RuntimeException("Failed to create vehicle: " + e.getMessage());    }  }}// 使用Vehicle car = VehicleFactory2.createVehicle(Car.class) ;car.drive() ;

JDK中的应用:



  • javax.xml.parsers.DocumentBuilderFactory.newInstance()



  • java.text.NumberFormat.getInstance()



  • java.util.Calendar.getInstance()  



Spring中的应用:

BeanFactoryApplicationContext 都是工厂模式的体现。

2.3 建造者模式(Builder)

Builder Pattern(建造者模式)是一种创建型设计模式,它允许通过分步骤的方式构建复杂对象。该模式将对象的构建过程与其表示分离,使得相同的构建过程可以创建不同的表示,提高了代码的灵活性和可复用性。

下面组装计算机实现

class Computer {  private String cpu;  private String memory;  private String storage;  private String graphicsCard;  private String operatingSystem;
private Computer(Builder builder) { this.cpu = builder.cpu; this.memory = builder.memory; this.storage = builder.storage; this.graphicsCard = builder.graphicsCard; this.operatingSystem = builder.operatingSystem; }
public static class Builder { private String cpu; private String memory; private String storage; private String graphicsCard; private String operatingSystem; public Builder(String cpu, String memory) { this.cpu = cpu; this.memory = memory; } public Builder storage(String storage) { this.storage = storage; return this; } public Builder graphicsCard(String graphicsCard) { this.graphicsCard = graphicsCard; return this; } public Builder operatingSystem(String operatingSystem) { this.operatingSystem = operatingSystem; return this; } public Computer build() { return new Computer(this); } } public void showConfiguration() { System.out.println("CPU: " + cpu); System.out.println("Memory: " + memory); System.out.println("Storage: " + storage); System.out.println("Graphics Card: " + graphicsCard); System.out.println("Operating System: " + operatingSystem); }}
public class BuilderPatternExample { public static void main(String[] args) { Computer computer = new Computer.Builder("Intel i7", "16GB") .storage("1TB SSD") .graphicsCard("NVIDIA RTX 4060") .operatingSystem("Windows 10") .build() ; computer.showConfiguration() ; }}

JDK中的应用:

  • StringBuilder

  • Stream.Builder

      

Spring中的应用:

  • BeanDefinitionBuilder创建BeanDefinition对象

  • UriComponentsBuilder构建URI

     

2.4 策略模式

策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。该模式让算法独立于使用它的客户而变化,提高了代码的灵活性和可维护性,适用于算法经常变化或需要频繁切换算法的场景。

如下通过策略模式实现出行方式

interface TravelStrategy {  void travel();  double calculateCost(double distance);  double calculateTime(double distance);}
class WalkStrategy implements TravelStrategy { public void travel() { System.out.println("步行."); } public double calculateCost(double distance) { return 0.0; } public double calculateTime(double distance) { return distance / 5.0; }}class BikeStrategy implements TravelStrategy { public void travel() { System.out.println("自行车🚲."); } public double calculateCost(double distance) { return 0.0; } public double calculateTime(double distance) { return distance / 15.0; }}class CarStrategy implements TravelStrategy { public void travel() { System.out.println("汽车🚕."); } public double calculateCost(double distance) { return distance * 0.5; } public double calculateTime(double distance) { return distance / 60.0; }}class AirplaneStrategy implements TravelStrategy { public void travel() { System.out.println("飞机✈."); } public double calculateCost(double distance) { return distance * 0.8; } public double calculateTime(double distance) { return distance / 800.0; }}class TravelPlanner { private TravelStrategy travelStrategy; public TravelPlanner(TravelStrategy travelStrategy) { this.travelStrategy = travelStrategy; } public void setTravelStrategy(TravelStrategy travelStrategy) { this.travelStrategy = travelStrategy; } public void planTravel() { travelStrategy.travel(); } public double calculateCost(double distance) { return travelStrategy.calculateCost(distance); } public double calculateTime(double distance) { return travelStrategy.calculateTime(distance); }}
public class StrategyPatternExample { public static void main(String[] args) { double distance = 100.0;
// 步行出行 TravelPlanner travelPlanner1 = new TravelPlanner(new WalkStrategy()); travelPlanner1.planTravel(); System.out.println("Cost: " + travelPlanner1.calculateCost(distance)); System.out.println("Time: " + travelPlanner1.calculateTime(distance));
// 自行车出行🚲 TravelPlanner travelPlanner2 = new TravelPlanner(new BikeStrategy()); travelPlanner2.planTravel(); System.out.println("Cost: " + travelPlanner2.calculateCost(distance)); System.out.println("Time: " + travelPlanner2.calculateTime(distance));
// 汽车出行🚕 TravelPlanner travelPlanner3 = new TravelPlanner(new CarStrategy()); travelPlanner3.planTravel(); System.out.println("Cost: " + travelPlanner3.calculateCost(distance)); System.out.println("Time: " + travelPlanner3.calculateTime(distance));
// 飞机出行✈ TravelPlanner travelPlanner4 = new TravelPlanner(new AirplaneStrategy()); travelPlanner4.planTravel(); System.out.println("Cost: " + travelPlanner4.calculateCost(distance)); System.out.println("Time: " + travelPlanner4.calculateTime(distance)); }}

说明:

  • 首先,通过实现TravelStrategy接口来定义不同的出行策略。

  • 然后,创建一个TravelPlanner对象(上下文),并根据需要选择不同的出行策略。

  • 最后,调用planTravel()方法来执行出行操作,并使用calculateCost()和calculateTime()方法来计算成本和时间。

     

优点:

  • 易于扩展:如果想添加一种新的出行方式,只需创建TravelStrategy的一个新实现类,而无需修改TravelPlanner类的代码。

  • 灵活切换:可以轻松地在不同的出行策略之间进行切换,以满足不同的出行需求。

  • 代码清晰:不同出行方式的逻辑被封装在各自的策略类中,使代码更加可读和易于维护。

     

JDK中的应用:


  • java.util.Comparator

     

Spring中的应用:

事务管理对象TransactionManager它定义了开始、提交和回滚事务的方法。Spring 提供了多种 PlatformTransactionManager 的实现,例如 JtaTransactionManager, DataSourceTransactionManager 等,以支持不同类型的数据源和技术。

2.5 观察者模式

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新,从而提高了应用程序的可维护性和重用性。

如下示例通过观察者模式实现股票价格变化后进行通知

interface StockObserver {  void update(double price);}
class Investor implements StockObserver { private String name; public Investor(String name) { this.name = name; } public void update(double price) { System.out.println(name + " 价格发生变化,当前价格: " + price); }}
interface StockSubject { void registerObserver(StockObserver observer); void removeObserver(StockObserver observer); void notifyObservers();}
class Stock implements StockSubject { private List<StockObserver> observers = new ArrayList<>(); private double price; public void registerObserver(StockObserver observer) { observers.add(observer); } public void removeObserver(StockObserver observer) { observers.remove(observer); } public void notifyObservers() { for (StockObserver observer : observers) { observer.update(price); } } public void setPrice(double price) { this.price = price; notifyObservers() ; }}
public class ObserverPatternExample { public static void main(String[] args) {    Stock stock = new Stock(); Investor investor1 = new Investor("Pack"); Investor investor2 = new Investor("XO");    Investor investor3 = new Investor("Pxg"); // 将投资者注册为观察员 stock.registerObserver(investor1); stock.registerObserver(investor2);    stock.registerObserver(investor3); //更新价格,并通知投资者 stock.setPrice(100.0);    stock.setPrice(105.0); // 删除观察者    stock.removeObserver(investor2); // 再次更新 stock.setPrice(110.0); }}

输出结果

说明:

  • 首先,创建一个Stock对象作为主题。

  • 创建多个Investor对象作为观察者。

  • 使用registerObserver方法将这些投资者注册到Stock对象中。

  • 当调用setPrice方法来更新股票价格时,所有已注册的投资者都会自动收到通知。

  • 可以使用removeObserver方法来移除不需要接收通知的投资者。

     

JDK中的应用:

  • java.util.Observer and java.util.Observable(从JDK9开始已经声明为过时的类)。

  • javax.swing.event.ChangeListener

     

Spring中的应用:

Spring中的事件监听机制,ApplicationEvent , ApplicationListener。

2.6 代理模式

代理模式是程序设计中的一种结构型设计模式,它为一个对象提供一种代理以控制对该对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以在不改变目标对象的情况下为其添加额外功能或控制访问,从而保护目标对象并降低系统耦合度。

代理模式,有静态代理,动态代理(基于接口,基于类CGLIB)实现,这里我们只介绍基于JDK的动态代理。

interface UserService {  void save() ;}class UserServiceImpl implements UserService {  public void save() {    System.out.println("UserService save method invoke...");  }}class UserServiceInvocationHandler implements InvocationHandler {  private Object target;  public UserServiceInvocationHandler(Object target) {    this.target = target;  }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    System.err.println("execute before...") ;    Object result = method.invoke(target, args);    System.err.println("execute after...") ;    return result ;  }}public class DynamicProxyExample {  public static void main(String[] args) {    UserService realService = new UserServiceImpl();    UserServiceInvocationHandler handler = new UserServiceInvocationHandler(realService);    UserService proxyService = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),        new Class<?>[] { UserService.class }, handler);    proxyService.save() ;  }}

说明:

JDK动态代理核心就2点:

  • 实现InvocationHandler接口,该接口就是用来扩展功能及目标方法的调用。

  • 使用Proxy创建目标接口的代理对象。

     

2.7 模板模式

模板模式是一种行为型设计模式,它定义了一个算法的骨架,并允许子类为骨架中的一个或多个步骤提供具体实现。通过这种模式,子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤,从而实现代码复用和算法扩展。

如下示例模拟饮品的制作

abstract class BeverageMaker {  public final void makeBeverage() {    prepareIngredients();    brew();    pourInCup();    addCondiments();  }  protected abstract void prepareIngredients();  protected abstract void brew();  protected void pourInCup() {    System.out.println("饮品倒入杯中");  }  protected abstract void addCondiments();}class CoffeeMaker extends BeverageMaker {  protected void prepareIngredients() {    System.out.println("准备咖啡豆及水");  }  protected void brew() {    System.out.println("煮咖啡");  }  protected void addCondiments() {    System.out.println("添加糖和牛奶");  }}
class TeaMaker extends BeverageMaker { protected void prepareIngredients() { System.out.println("准备茶叶和水"); } protected void brew() { System.out.println("使用沸水泡茶"); } protected void addCondiments() { System.out.println("添加柠檬🍋"); }}public class TemplateMethodPatternDemo { public static void main(String[] args) { BeverageMaker coffeeMaker = new CoffeeMaker(); System.out.println("制作咖啡: "); coffeeMaker.makeBeverage();
System.out.println("-----------------");
BeverageMaker teaMaker = new TeaMaker(); System.out.println("制作茶: "); teaMaker.makeBeverage(); }}

说明:

BeverageMaker 是一个抽象类,其中 makeBeverage 是一个模板方法。它定义了制作饮品的算法骨架,即先准备原料,然后制作饮品,将饮品倒入杯中,最后添加调料。这个方法是最终的(final),不允许子类修改这个过程的顺序。

prepareIngredients、brew 和 addCondiments 是抽象方法。因为不同的饮品需要不同的原料准备、冲泡和添加调料的方法,这些方法由具体的子类来实现。

pourInCup 方法是一个具有默认实现的方法。对于大多数饮品来说,将饮品倒入杯中的操作是相似的,但如果子类有特殊要求,也可以覆盖这个方法。

CoffeeMaker 和 TeaMaker 是 BeverageMaker 的具体子类。它们分别覆盖了 prepareIngredients、brew 和 addCondiments 方法,以实现制作咖啡和茶的具体步骤。

JDK中的应用:

java.util.AbstractList 与 java.util.AbstractMap

Spring中的应用:

JdbcTemplate 与 RestTemplate




以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

强大!SQL解析神器JSQLParser

请牢记SpringBoot这7个强大的隐藏Bean

手写@RefreshScope,很简单嘛!

太强了!SpringBoot结合Camel实现各种协议路由规则定义

接口优化!Spring Boot 整合 Kryo 提升接口性能,传输数据更小

Map你只会用put,get?试试这些高级方法

自己动手实现精简版SpringBoot原来如此简单

在 Spring Boot 中加载属性文件的7种方法

重磅!Spring Boot3.4 结构化日志,强大的日志记录技术

新功能来了!Spring非常实用的5个功能

生产环境Spring Boot切记要关闭这个开关

提升性能:Java工程师必备的20条SQL最佳实践

Jackson才是王!SpringBoot优雅的控制JSON数据

Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
 最新文章