凡客诚品网站地址,建筑设计毕业设计作品,网站上的地图怎么做,区块链app定制开发文章目录 Pre概述依赖倒置原则与解耦设计与实现1. 定义接口来隔离调用方与实现类2. 实现类DynamicStubFactory3. 调用方与实现类的解耦 依赖注入与SPI的解耦依赖注入SPI#xff08;Service Provider Interface#xff09; 总结 Pre
Simple RPC - 01 框架原理及总体架构初探 … 文章目录 Pre概述依赖倒置原则与解耦设计与实现1. 定义接口来隔离调用方与实现类2. 实现类DynamicStubFactory3. 调用方与实现类的解耦 依赖注入与SPI的解耦依赖注入SPIService Provider Interface 总结 Pre
Simple RPC - 01 框架原理及总体架构初探
Simple RPC - 02 通用高性能序列化和反序列化设计与实现
Simple RPC - 03 借助Netty实现异步网络通信
Simple RPC - 04 从零开始设计一个客户端上 概述
接 Simple RPC - 04 从零开始设计一个客户端上 我们继续分析 依赖倒置和SPI是如何实现的。 依赖倒置原则与解耦
在软件设计中依赖倒置原则Dependence Inversion Principle, DIP 是SOLID原则之一。它主张高层模块调用者不应依赖于低层模块实现类而是两者都应该依赖于抽象接口或抽象类。这意味着具体的实现细节应当与高层业务逻辑分离通过接口来隔离依赖关系从而提高代码的可维护性、可扩展性和可复用性。
设计模式 - 六大设计原则之ISP接口隔离原则 设计与实现
在这个RPC框架的设计中通过定义接口来解耦调用方和具体实现完全符合依赖倒置原则。
我们来看下是如何应用DIP来解耦的。
1. 定义接口来隔离调用方与实现类
public interface StubFactory {T T createStub(Transport transport, ClassT serviceClass);
}StubFactory接口定义了创建桩的方法而具体的实现类DynamicStubFactory实现了该接口。
2. 实现类DynamicStubFactory
public class DynamicStubFactory implements StubFactory {// 实现 createStub 方法的逻辑
}DynamicStubFactory实现了StubFactory接口提供了实际的桩生成逻辑。
3. 调用方与实现类的解耦
在调用方NettyRpcAccessPoint中我们并不直接依赖于具体的DynamicStubFactory而是依赖于StubFactory接口。调用方通过接口与实现类进行交互这样如果以后需要更换不同的StubFactory实现只需更改实现类而无需修改调用方的代码。
public class NettyRpcAccessPoint {private final StubFactory stubFactory;public NettyRpcAccessPoint(StubFactory stubFactory) {this.stubFactory stubFactory;}public T T createStub(Transport transport, ClassT serviceClass) {return stubFactory.createStub(transport, serviceClass);}
}依赖注入与SPI的解耦
依赖注入
通常情况下依赖注入如Spring框架可以帮助我们实现这种解耦通过配置或注解框架会自动将具体的实现注入到调用方中。但在不使用Spring的情况下我们可以使用Java内置的SPI机制来实现类似的解耦。
SPIService Provider Interface
SPI机制通过在META-INF/services/目录下配置接口的实现类在运行时动态加载这些实现类实现依赖倒置。 配置文件 在META-INF/services/目录下创建一个文件文件名是接口的完全限定名例如com.github.liyue2008.rpc.client.StubFactory。文件内容是接口的实现类名例如com.github.liyue2008.rpc.client.DynamicStubFactory。 SPI加载实现类 /*** 提供服务加载功能的支持类特别是处理单例服务* author artisan*/
public class ServiceSupport {/*** 存储单例服务的映射确保每个服务只有一个实例*/private final static MapString, Object singletonServices new HashMap();/*** 加载单例服务实例** param service 服务类的Class对象* param S 服务类的类型参数* return 单例服务实例* throws ServiceLoadException 如果找不到服务实例*/public synchronized static S S load(ClassS service) {return StreamSupport.stream(ServiceLoader.load(service).spliterator(), false).map(ServiceSupport::singletonFilter).findFirst().orElseThrow(ServiceLoadException::new);}/*** 加载所有服务实例** param service 服务类的Class对象* param S 服务类的类型参数* return 所有服务实例的集合*/public synchronized static S CollectionS loadAll(ClassS service) {return StreamSupport.stream(ServiceLoader.load(service).spliterator(), false).map(ServiceSupport::singletonFilter).collect(Collectors.toList());}/*** 对服务实例进行单例过滤** param service 服务实例* param S 服务类的类型参数* return 单例过滤后的服务实例如果该服务是单例的并且已有实例存在则返回已存在的实例*/SuppressWarnings(unchecked)private static S S singletonFilter(S service) {if(service.getClass().isAnnotationPresent(Singleton.class)) {String className service.getClass().getCanonicalName();Object singletonInstance singletonServices.putIfAbsent(className, service);return singletonInstance null ? service : (S) singletonInstance;} else {return service;}}
}
调用ServiceSupport.load(StubFactory.class)时SPI机制会查找META-INF/services/目录下对应的配置文件加载其中指定的实现类实例。 总结
通过依赖倒置原则DIP和SPI机制我们有效地解耦了调用方与实现类。在这个RPC框架中StubFactory接口及其实现类DynamicStubFactory之间的依赖关系被逆转调用方只依赖接口而不直接依赖具体实现。SPI机制进一步解耦了调用方与实现类的实例化使得在运行时可以动态加载实现类这为框架的扩展性和灵活性提供了强有力的支持。
通过这种设计框架可以很容易地替换StubFactory的实现而不影响调用方保持了代码的高可维护性和 扩展性。