本文转载自微信公众号《狼王编程》,作者狼王。转载本文请联系狼王编程公众号。如果说普通重构是为了消除代码的臭味,那么高级重构就是为了消除架构的臭味。组件需要重构。当你拿到公司老项目的旧代码,需要打开或者重构的时候,你就会头大,无从下手。之前一直都是这种状态,但是慢慢的熟悉了一些重构的思路和方法之后,就可以稍微舒服一些了。接下来开始讲重构,然后在重构中重点讲SPI接口。先给大家看一个最近重构的一个组件——分布式存储,使用SPI接口。好家伙,重构前的代码结构,所有的第三方存储都写在一个模块里,各种阿里云,腾讯云,华为云等等,这样的代码结构前期可能不需要经常扩容。开启时仍然可用。但是当一个新的需求来的时候,比如我遇到的:需要支持多云多账号的上传下载功能。这是因为不同账号的权限,安全认证等都是一样的,所以在某个时刻,提出了这个需求,就是你可以上传到你想上传到哪个云的任何一个账号。然后拿到代码,看架构。可以在此基础上完成需求,但是扩展起来很麻烦,而且代码会越来越繁琐,架构也会越来越复杂,不清晰。于是干脆借此机会重构,和其他同事一起讨论。我决定把模块分成SPI。好处是根据自己要用的东西引入相应的依赖,这样代码结构更清晰,后续更容易。展开!以下是重构后的总体架构:是不是更清晰了?哪怕某个云存储需要增加新的功能,或者需要兼容更多的云,也会变得更加容易。好吧,让我们开始谈论重构。什么是重构?重构就是通过调整程序代码来提高软件的质量和性能,使程序的设计模式和结构更加合理。提高软件的可扩展性和可维护性。重构最重要的思想是让普通的程序员也能写出优秀的程序。将优化代码质量的过程分解成一个个小步骤,让重构项目的巨大工作量变成修改变量名、提取函数、提取接口等简单的工作目标。作为一名普通的程序员,你可以通过实现这些容易实现的工作目标来提高自己的编码能力,加深对项目的理解,从而为最高层次的重构打下基础。而且,高层次的重构仍然是由无数个小目标组成的,而不是长期的、大规模的实施。重构的本质是极限编程的一部分,极限编程的完整实现可以最大化重构的价值。极限编程本身就提倡拥抱变化,增强适应性,所以将极限编程中的功能进行分解,以适应项目的需要和团队的现状,才是最好的操作方式。重构的重点是重复代码、函数太长、类太大、参数列表太长、发散变化、鸟枪修改、附件复杂、数据泥球、基本类型偏执、并行继承系统、冗余类等。一些常见的用过的或者比较基础的例子:我觉得一些基本的原则还是需要了解尽量避免创建Java对象过多过长尽量使用局部变量尽量使用StringBuilder和StringBuffer进行字符串拼接尽量减少变量的重复计算尝试在finally块中释放资源尝试缓存经常使用的对象,及时将不用的对象设置为null尝试考虑使用静态方法尝试在适当的地方使用单例尝试使用final修饰符以下是关于类和方法的优化:提取的重复代码冗长方法的分割,嵌套的条件分支,或循环递归的优化,常量的提取在类或继承系统中,提取继承系统中重复的属性和方法,传递给父类。今天的主角是SPI,请往下看SPI!什么是SPI?SPI的全称是ServiceProviderInterface。它是Java提供的一组API,由第三方实现或扩展。它可用于启用框架扩展和替换组件。它是一种服务发现机制,在ClassPath路径下的META-INF/services文件夹中搜索文件,并自动加载文件中定义的类。这种机制为许多框架扩展提供了可能性。比如Dubbo、JDBC中都使用了SPI机制。以下是SPI的机制流程。SPI实际上是一种基于接口编程+策略模式+配置文件组合实现的动态加载机制。系统设计的每一个抽象往往有许多不同的实现方案。在面向对象设计中,一般建议模块间基于接口进行编程,模块间不要硬编码实现类。一旦代码涉及到具体的实现类,就违反了可插拔性原则。如果需要更换一个实现,就需要修改代码。为了实现模块组装时不能在程序中动态指定,需要一种服务发现机制。SPI就是提供这样一种机制:一种为接口寻找服务实现的机制。有点类似于IOC的思想,就是把汇编的控制权移出程序。这种机制在模块化设计中尤为重要。所以SPI的核心思想就是解耦。SPI使用介绍要使用JavaSPI,一般需要遵循以下约定:当服务提供者提供接口的具体实现时,在META-INF/services目录下创建一个以接口全限定名命名的服务的jar包。文件,内容是实现类的完全限定名;接口实现类所在的jar包放在主程序的类路径下;主程序通过java.util.ServiceLoder动态加载实现模块,它会扫描META-INF/services目录下的配置文件找到实现类的完全限定名,并将该类加载到JVM中;SPI实现类必须携带无参构造函数;SPI使用场景一般适用于:调用方根据实际使用需要启用和扩展类,或者替换框架的实现策略。下面是比较常见的例子:数据库驱动加载接口实现类加载JDBC加载不同类型的数据库驱动日志门面接口实现类加载SLF4J加载不同提供者日志实现类SpringSpring被广泛使用的SPI,例如:Servlet3的ServletContainerInitializer实现.0规范,自动类型转换TypeConversionSPI(ConverterSPI,FormatterSPI)等DubboDubbo也使用SPI实现框架的扩展,但不支持Java提供的原生SPI。做封装让用户扩展实现Filter接口SPI简单例子先定义接口类packagecom.test.spi.learn;importjava.util.List;publicinterfaceSearch{publicList
