项目的改造——RemoveButterKnife插件代码的重构

栏目: 后端 · 发布时间: 5年前

内容简介:这篇文章记述了我的插件RemoveButterKnife的代码改进过程以及思路,关于插件,各位可以看近期想给原来的插件RemoveButterKnife加入一些新的功能,发现以前的代码没有使用任何的设计模式,全部功能都写在一起,对于新功能的添加来说十分糟糕。趁此机会重构了一下代码,在此记录过程。插件主要分为三个部分

前言

这篇文章记述了我的插件RemoveButterKnife的代码改进过程以及思路,关于插件,各位可以看 RemoveButterKnife代码库 ,关于文章,可以看 RemoveButterKnife从构思到实现

原因

近期想给原来的插件RemoveButterKnife加入一些新的功能,发现以前的代码没有使用任何的设计模式,全部功能都写在一起,对于新功能的添加来说十分糟糕。趁此机会重构了一下代码,在此记录过程。

具体步骤

插件主要分为三个部分

1. 主插件入口部分

2. 代码寻找/处理部分

3. 代码生成部分

1. 主插件入口部分

我们首先看第一部分,主入口部分,这部分内容主要代码如下

   @Override
    public void actionPerformed(AnActionEvent event) {
        try {
        project = event.getData(PlatformDataKeys.PROJECT);
        Editor editor = event.getData(PlatformDataKeys.EDITOR);
        file = PsiUtilBase.getPsiFileInEditor(editor, project);
        mFactory = JavaPsiFacade.getElementFactory(project);
        mClass = getTargetClass(editor,file);
        Document document = editor.getDocument(); //以上都是从上下文中获取的辅助对象,具体可以查阅idea plugin文档
        new DeleteAction(project,file,document,mClass).execute();//执行删除操作
        }catch (Exception e){
            e.printStackTrace();
        }
    }

这部分主要是获取一些需要处理的上下文变量以及下发操作给删除操作,不需要进行处理

2. 代码寻找/处理部分

第二部分,也是我们的主要逻辑所在的部分,主要代码逻辑如下

1.寻找import相关代码,并把行号存入列表

2.寻找Api调用代码,存入行号

3.寻找bind相关代码,存入行号,分离id和name以及type,分别存入对应集合

4.删除上述生成的行号集合对应代码

5.将生成findview的指令下发给代码生成类

通过上述逻辑,我们可以看到,1-3步是逻辑不相关部分,没有前后顺序,也没有相互依赖。

那么,我们就可以通过责任链的模式来对1-3步进行拆分。

首先,我们创建一个BaseChain作为基类

BaseChain主要分为三个部分

1.成员部分

2.处理逻辑部分

3.设置子链部分

代码如下

public abstract class BaseChain {
   protected BaseChain next;
   protected String[] currentDoc;
   protected List<Integer> deleteLineNumbers;
   protected Map<String,String> nameAndIdMap;//第一部分,声明成员
   public void setNext(BaseChain next){
      this.next = next;
   }//设置下一步
    final public void handle(String[] currentDoc,List deleteLineNumbers,Map nameAndIdMap){
        this.deleteLineNumbers = deleteLineNumbers;
        this.nameAndIdMap = nameAndIdMap;
        this.currentDoc = currentDoc;
        process();
        dispatcher();
    }//内部处理逻辑,无法被子类修改
    abstract public void process();//子类需要实现的处理部分
    private void dispatcher(){
        if(next != null) {
            next.handle(currentDoc, deleteLineNumbers, nameAndIdMap);
        }
    }//转发逻辑
}

然后继续创建子Chain类

1.寻找import相关代码,并把行号存入列表

2.寻找Api调用代码,存入行号

3.寻找bind相关代码,存入行号,分离id和name以及type,分别存入对应集合

我们这里拿寻找import相关代码,并把行号存入列表来举例

public class DetectImportChain extends BaseChain{
 
    public static final String IMPORT_BUTTERKNIFE_BIND = "import butterknife.Bind;";
    public static final String IMPORT_BUTTERKNIFE_INJECT_VIEW = "import butterknife.InjectView;";
    public static final String IMPORT_BUTTERKNIFE_BUTTER_KNIFE = "import butterknife.ButterKnife;";
    public static final String IMPORT_BUTTERKNIFE_BIND_VIEW = "import butterknife.BindView;";//定义了我们需要寻找的语句
 
    @Override
    public void process() {
        for (int i = 0;i < currentDoc.length;i++){
            if (currentDoc[i].equals(IMPORT_BUTTERKNIFE_BIND)||currentDoc[i].equals(IMPORT_BUTTERKNIFE_BIND_VIEW)||currentDoc[i].equals(IMPORT_BUTTERKNIFE_BUTTER_KNIFE)||currentDoc[i].equals(IMPORT_BUTTERKNIFE_INJECT_VIEW)){
                deleteLineNumbers.add(i);
            }
        }
    }//进行处理
}

有了对应的子类,我们还需要加上junit测试,例如

  @Test
    public void test_with_api_use() {
        currentDoc[0] = "NotUseApi();";
        currentDoc[1] = "ButterKnife.useApi();";
        chain.handle(currentDoc,deleteLineNumbers,nameAndIdMap);
        int expect = 1;
        int result = deleteLineNumbers.size();
        assertEquals(expect,result);
    }

这时候我们发现,在这几个子类的测试中,每次都需要初始化一些集合,每个都写十分麻烦,于是我们将其抽出来成为基类,代码如下

class BaseTest {
   protected Map<String,String> nameAndIdMap;
   protected Map<Integer,String> typeAndNameMap;
   protected String[] currentDoc;
   protected List<Integer> deleteLineNumbers;
   @Before
   public void init(){
       nameAndIdMap = new LinkedHashMap<>();
       typeAndNameMap = new LinkedHashMap<>();
       deleteLineNumbers = new ArrayList<>();
       currentDoc = new String[10];
   }
}

这样,我们的测试类直接继承这个基类就可以省下一些代码量了。

删除对应行代码

此部分主要是调用idea的api进行处理,所以我们这里不做过多修改,把方法保留在action里即可。

3生成findViewByid部分

生成代码的逻辑是寻找到文本的特定位置然后依据上述找到的id,name等,进行语句的插入

这一部分前期只负责生成findViewById语句,所以做成单个 工具 类没有问题。

但是随着项目的扩展,我们还会生成更多种类的代码,例如onclick对应的代码序列等,这时我们就需要对其进行重构。

分析行为

该部分的主要操作是寻找代码指定部分,并使用信息生成代码

1.拆分行为

我们可以拆分为两个步骤

1.寻找特定部分

2.按照分类生成代码

生成代码部分可以分为基础行为和特定行为,基础行为是指生成代码的api调用,特定行为是指生成的代码根据种类不同而不同

2.拆分方案

根据上述分析,我们可以使用策略模式进行优化,每一种生成代码都有对应的策略,我们使用的时候只需要根据类别使用不同的策略类来生成即可

3.具体代码

由于这部分正在开发中,所以暂时没有完整的代码,后续待补充

对比

我们可以从重构前/后的目录结构来对比重构的效果

重构之前

项目的改造——RemoveButterKnife插件代码的重构

重构之后

项目的改造——RemoveButterKnife插件代码的重构

可能会有人问了,重构后感觉复杂了很多,但是从逻辑的维度上来说,一个熟悉 设计模式程序员 可以很快/方便的阅读重构后的代码,而重构前的代码虽然看起来文件少,但是所有逻辑都在一个文件中,往往会让人无法阅读/理解


以上所述就是小编给大家介绍的《项目的改造——RemoveButterKnife插件代码的重构》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Redis 深度历险:核心原理与应用实践

Redis 深度历险:核心原理与应用实践

钱文品 / 电子工业出版社 / 2019-1 / 79

Redis 是互联网技术架构在存储系统中使用得最为广泛的中间件,也是中高级后端工程师技术面试中面试官最喜欢问的工程技能之一,特别是那些优秀的互联网公司,通常要求面试者不仅仅掌握 Redis 基础用法,还要理解 Redis 内部实现的细节原理。《Redis 深度历险:核心原理与应用实践》作者老钱在使用 Redis 上积累了丰富的实战经验,希望帮助更多后端开发者更快、更深入地掌握 Redis 技能。 ......一起来看看 《Redis 深度历险:核心原理与应用实践》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码