热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

什么是重构法

本篇内容介绍了“什么是重构法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情

本篇内容介绍了“什么是重构法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

重构前的代码结构

好家伙,所有的第三方存储都是写在一个模块中的,各种阿里云,腾讯云,华为云等等,这样的代码架构在前期可能在不需要经常扩展,二开的时候,还是能用的。

但是当某个新需求来的时候,比如我遇到的:需要支持多个云的多个账号上传下载功能,这个是因为在不同的云上,不同账号的权限,安全认证等都是不太一样的,所以在某一刻,这个需求就被提出来了,也就是你想上传到哪个云的哪个账号都可以。

然后拿到这个代码,看了下这样的架构,可能在这样的基础上完成需求也是没有问题的,但是扩展很麻烦,而且代码会越来越繁重,架构会越来越复杂,不清晰。

所以我索性趁着这个机会,就重构一把,和其他同事也商量了下,决定分模块,SPI化,好处就是根据你想使用的引入对应的依赖,让代码架构更加清晰,后续更加容易扩展了!下面就是重构后的大体架构:

什么是重构法

是不是清楚多了,之后哪怕某个云存储需要增加新功能,或者需要兼容更多的云也是比较容易的了。

好了,下面就让我们开始讲讲重构大法~

重构

重构是什么?

重构(Refactoring)就是通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。

重构最重要的思想就是让普通程序员也能写出优秀的程序。

把优化代码质量的过程拆解成一个个小的步骤,这样重构一个项目的巨大工作量就变成比如修改变量名、提取函数、抽取接口等等简单的工作目标。

作为一个普通的程序员就可以通过实现这些易完成的工作目标来提升自己的编码能力,加深自己的项目认识,从而为最高层次的重构打下基础。

而且高层次的重构依然是由无数个小目标构成,而不是长时间、大规模地去实现。

重构本质是极限编程的一部分,完整地实现极限编程才能最大化地发挥重构的价值。而极限编程本身就提倡拥抱变化,增强适应性,因此分解极限编程中的功能去适应项目的需求、适应团队的现状才是最好的操作模式。

重构的重点

重复代码,过长函数,过大的类,过长参数列,发散式变化,霰弹式修改,依恋情结,数据泥团,基本类型偏执,平行继承体系,冗余类等

下面举一些常用的或者比较基础的例子:

一些基本的原则我觉得还是需要了解的

  • 尽量避免过多过长的创建Java对象

  • 尽量使用局部变量

  • 尽量使用StringBuilder和StringBuffer进行字符串连接

  • 尽量减少对变量的重复计算

  • 尽量在finally块中释放资源

  • 尽量缓存经常使用的对象

  • 不使用的对象及时设置为null

  • 尽量考虑使用静态方法

  • 尽量在合适的场合使用单例

  • 尽量使用final修饰符

下面是关于类和方法优化:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 重复代码的提取

  3. 冗长方法的分割

  4. 嵌套条件分支或者循环递归的优化

  5. 提取类或继承体系中的常量

  6. 提取继承体系中重复的属性与方法到父类

这里先简单介绍这些比较常规的重构思想和原则,方法,毕竟今天的主角是SPI,下面有请SPI登场!

SPI

什么是SPI?

SPI全称Service Provider  Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。

它是一种服务发现机制,它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。

下面就是SPI的机制过程

什么是重构法

SPI实际上是基于接口的编程+策略模式+配置文件组合实现的动态加载机制。

系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。

一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦。

SPI使用介绍

要使用Java SPI,一般需要遵循如下约定:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services目录下创建一个以接口全限定名`为命名的文件,内容为实现类的全限定名;

  3. 接口实现类所在的jar包放在主程序的classpath中;

  4. 主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM;

  5. SPI的实现类必须携带一个不带参数的构造方法;

SPI使用场景

概括地说,适用于:调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略

以下是比较常见的例子:

数据库驱动加载接口实现类的加载 JDBC加载不同类型数据库的驱动

日志门面接口实现类加载 SLF4J加载不同提供商的日志实现类

Spring  Spring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type  Conversion SPI(Converter SPI、Formatter SPI)等

Dubbo Dubbo中也大量使用SPI的方式实现框架的扩展, 不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口

SPI简单例子

先定义接口类

package com.test.spi.learn; import java.util.List;  public interface Search {     public List searchDoc(String keyword);    }

文件搜索实现

package com.test.spi.learn; import java.util.List;  public class FileSearch implements Search{     @Override     public List searchDoc(String keyword) {         System.out.println("文件搜索 "+keyword);         return null;     } }

数据库搜索实现

package com.test.spi.learn; import java.util.List;  public class DBSearch implements Search{     @Override     public List searchDoc(String keyword) {         System.out.println("数据库搜索 "+keyword);         return null;     } }

接下来可以在resources下新建META-INF/services/目录,然后新建接口全限定名的文件:com.test.spi.learn.Search

里面加上我们需要用到的实现类

com.test.spi.learn.FileSearch com.test.spi.learn.DBSearch

然后写一个测试方法

package com.test.spi.learn; import java.util.Iterator; import java.util.ServiceLoader;  public class TestCase {     public static void main(String[] args) {         ServiceLoader s = ServiceLoader.load(Search.class);         Iterator iterator = s.iterator();         while (iterator.hasNext()) {            Search search =  iterator.next();            search.searchDoc("hello world");         }     } }

可以看到输出结果:

文件搜索 hello world 数据库搜索 hello world

SPI原理解析

通过查看ServiceLoader的源码,梳理了一下,实现的流程如下:

应用程序调用ServiceLoader.load方法  ServiceLoader.load方法内先创建一个新的ServiceLoader,并实例化该类中的成员变量,包括以下:

loader(ClassLoader类型,类加载器) acc(AccessControlContext类型,访问控制器)  providers(LinkedHashMap

应用程序通过迭代器接口获取对象实例  ServiceLoader先判断成员变量providers对象中(LinkedHashMap

如果有缓存,直接返回。如果没有缓存,执行类的装载,实现如下:

(1)  读取META-INF/services/下的配置文件,获得所有能被实例化的类的名称,值得注意的是,ServiceLoader可以跨越jar包获取META-INF下的配置文件

(2) 通过反射方法Class.forName()加载类对象,并用instance()方法将类实例化。

(3) 把实例化后的类缓存到providers对象中,(LinkedHashMap

总结

优点

使用SPI机制的优势是实现解耦,使得接口的定义与具体业务实现分离,而不是耦合在一起。应用进程可以根据实际业务情况启用或替换具体组件。

缺点

不能按需加载。虽然ServiceLoader做了延迟载入,但是基本只能通过遍历全部获取,也就是接口的实现类得全部载入并实例化一遍。如果你并不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。

获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类。

多个并发多线程使用 ServiceLoader 类的实例是不安全的。

加载不到实现类时抛出并不是真正原因的异常,错误很难定位。

看到上面这么多的缺点,你肯定会想,有这些弊端为什么还要使用呢,没错,在重构的过程中,SPI接口化是一个非常有用的方式,当你需要扩展的时候,适配的时候,越早的使用你就会受利越早,在一个合适的时间,恰当的机会的时候,就鼓起勇气,重构吧!

“什么是重构法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程笔记网站,小编将为大家输出更多高质量的实用文章!


推荐阅读
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 解决Sharepoint 2013运行状况分析出现的“一个或多个服务器未响应”问题的方法
    本文介绍了解决Sharepoint 2013运行状况分析中出现的“一个或多个服务器未响应”问题的方法。对于有高要求的客户来说,系统检测问题的存在是不可接受的。文章详细描述了解决该问题的步骤,包括删除服务器、处理分布式缓存留下的记录以及使用代码等方法。同时还提供了相关关键词和错误提示信息,以帮助读者更好地理解和解决该问题。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
author-avatar
长发及腰和我娶你D有毛关系
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有