SPI + 工厂设计模式

1. 什么是 SPI ?有什么用? SPI(Service Provider Interface)是Java提供的一种用于实现可扩展性和插件机制的接口规范。它允许开发者定义或使用服务提供者(Service Providers),并且可以在运行时动态地加载这些服务,而不需要在编译时将服务的实现绑定到应用程序中。这种机制特别适合那些需要根据配置或条件加载不同服务实现的场景,例如数据库驱动、加密算法等。 1.1. SPI 的核心概念: 服务接口(Service Interface):定义服务提供者需要实现的接口。例如,java.sql.Driver就是一个常见的服务接口,所有的数据库驱动都必须实现这个接口。 服务提供者(Service Provider):服务接口的具体实现。例如,不同数据库厂商的驱动程序就是服务提供者。 服务加载器(Service Loader):用于发现和加载服务提供者的机制。Java 提供了 java.util.ServiceLoader 来查找服务提供者。 配置文件:SPI 使用一个特殊的配置文件来声明服务提供者的实现类。配置文件放置在 META-INF/services/ 目录下,文件名为服务接口的全限定名,文件内容为实现类的全限定名。例如,META-INF/services/java.sql.Driver 这个文件可能包含多个 JDBC 驱动实现类。 1.2. SPI的工作原理: 应用程序通过 ServiceLoader 查找指定服务接口的实现。 JVM在 META-INF/services/ 目录下查找对应的服务配置文件。 服务加载器解析配置文件并实例化指定的服务提供者类。 2. 实现 具体实现方法如下: 指定 SPI 的配置目录,并且将配置再分为系统内置 SPI 和用户自定义 SPI,便于区分优先级和维护。 编写 SpiLoader 加载器,实现读取配置、加载实现类的方法。 用 Map 来存储已加载的配置信息 键名 => 实现类。 通过 Hutool 工具库提供的 ResourceUtil.getResources 扫描指定路径,读取每个配置文件,获取到 键名 => 实现类 信息并存储在 Map 中。 定义获取实例方法,根据用户传入的接口和键名,从 Map 中找到对应的实现类,然后通过反射获取到实现类对象。可以维护一个对象实例缓存,创建过一次的对象从缓存中读取即可。 重构序列化器工厂,改为从 SPI 加载指定的序列化器对象。 使用静态代码块调用 SPI 的加载方法,在工厂首次加载时,就会调用 SpiLoader 的 load 方法加载序列化器接口的所有实现类,之后就可以通过调用 getInstance 方法获取指定的实现类对象了。 ...

十月 9, 2024 · 3 分钟

常见设计模式及代码示例

一、七大设计原则 1.开放-封闭原则 (Open-Closed Principle, OCP) 原则描述:软件实体(类、模块、函数等)应当对扩展开放,对修改关闭。也就是说,当需求变化时,应尽量通过添加新代码来实现功能增强,而不是修改现有的代码。 举例:在开发图形库时,如果需要支持多种形状的绘制,可以创建一个抽象的Shape接口,并为每种形状(如Circle、Rectangle)提供具体实现类。当要增加新的形状时,只需新增一个遵循Shape接口的类即可,无需修改已有代码。 2.单一职责原则 (Single Responsibility Principle, SRP) 原则描述:一个类或模块应该有且仅有一个改变它的原因。简单来说,每个类都应该专注于做一件事,并把它做好。 举例:在员工管理系统中,Employee类只负责处理与员工基本信息相关的操作,而薪资计算的功能则由另一个SalaryCalculator类负责,这样修改员工信息不会影响到薪资计算逻辑,反之亦然。 3.里氏替换原则 (Liskov Substitution Principle, LSP) 原则描述:子类型必须能够替换它们的基类型。即继承体系中的子类在父类出现的地方能够无二义性地替代父类工作,而不改变程序正确性。 举例:假设有一个Shape基类和派生出的Square子类,Square是一个特殊的Rectangle,但在使用Shape的地方,无论传入的是Rectangle还是Square,都能正常执行面积计算等方法,而不会因多态调用产生错误结果。 4.依赖倒置原则 (Dependency Inversion Principle, DIP) 原则描述:高层模块不应该依赖于低层模块,两者都应依赖于抽象。抽象不应依赖于细节,细节应依赖于抽象。 举例:在应用程序中,控制器类(高层模块)不直接依赖于数据库访问类(低层模块),而是共同依赖于一个数据访问接口(抽象)。这样,在更换数据库技术时,只需重新实现接口,而不用修改控制器。 5.接口隔离原则 (Interface Segregation Principle, ISP) 原则描述:客户端不应该被迫依赖它不需要的方法。接口应该小而专,每个接口代表一种独立的角色或职责。 举例:一个大的UserService接口可以拆分为更细粒度的UserAuthentication, UserProfileManagement, UserPermissionControl等接口,各个组件根据实际需求选择合适的接口进行依赖,避免接口方法的冗余和浪费。 6.合成/聚合复用原则 (Composition/Aggregate Reuse Principle, CRP) 原则描述:优先使用组合或者聚合关系而非继承来达到复用的目的。换句话说,尽量使用“拥有”关系(包含实例)而不是“是”关系(继承)来复用行为。 举例:在构建一个系统时,可以创建一个Car类,其中包含多个部件对象(如Engine、Wheel等),而不是让Car从这些部件继承。这样,部件的变化不会直接影响到Car类,增强了系统的灵活性。 7.迪米特法则 (Law of Demeter, LoD) 原则描述:也称为最少知识原则,即一个对象应当尽可能少地了解其他对象。一个对象只需要知道与其直接交互的对象即可。 举例:在一个订单系统中,Order类只需与Customer类交互获取必要的信息,而不直接访问Customer的地址信息,而是通过Customer.getAddress()间接获取,这样减少了对象之间的耦合度。 二、UML类图 UML类图(Unified Modeling Language Class Diagram)是面向对象分析与设计中最为常用的图形化建模工具之一,它主要用于描述系统的静态结构。在UML类图中,主要元素包括类、接口以及它们之间的关系。下面是一些关键概念和组成部分: 类(Class): 类名:通常以粗体表示,在顶部书写。 属性(Attributes):类的成员变量或数据字段,描述类所拥有的状态信息,一般在类名称下方列出,并可标注可见性(+公共、-私有、#保护)和其他属性如数据类型、初始值等。 操作(Operations):类的方法或行为,位于类定义的底部,同样可以标记可见性和参数列表。 关系(Relationships): 继承(Inheritance):用一个带空心三角形箭头的直线表示,箭头指向基类,用于表示子类对父类的继承关系。 实现(Realization):类实现接口时,使用虚线箭头,箭头指向接口,表示类实现了接口的所有方法。 关联(Association):用来表示类之间的连接,可以通过线条上的角色名和多重性(0.., 1.., 1, etc.)来详细说明关联关系。 聚合(Aggregation):特殊的关联关系,表示整体与部分的关系,通常通过空心菱形箭头表示(箭头从整体指向部分)。 组合(Composition):比聚合更强的关系,表示部分不能脱离整体存在,用实心菱形箭头表示。 依赖(Dependency):表示一种较弱的“使用”关系,即一个元素的变化可能会影响到另一个元素,用带箭头的虚线表示。 接口(Interface): ...

三月 26, 2024 · 23 分钟