第1篇-如何编写一个面试时能拿的出手的开源项目?

千纸鹤 6天前   阅读数 10 0

编写一个不错的开源项目至少有3个好处:

(1)练技术,长经验

(2)面试时展现自己的Coding能力

(3)获取成就感

练技术,长经验是最实在的,不过如果自己有一个好的开源项目,还可以在开源中国或github上开源,让更多人受益,也许能获得许多的Star,让自已获得满足,激励自己完善项目功能。在面试时,也可以在简历显眼的位置给出开源项目名称和Git地址。面试官一般都会去查看,这是程序员实力的最好见证,是自己Coding能力的最好见证。所以如果要编写一个面试时拿的出手的项目,首先需要做到“规范”,下面谈一下我眼中规范的开源项目。

1 项目规范

1.1 规范编写README.md

README.md文件是一个项目的入门手册,里面介绍了整个项目的使用、功能等等。所以README文件写得好不好,关系到这个项目能不能更容易的被其他人了解和使用。

首先最起码要做到的就是格式要清晰,让人一眼能看出层次感,例如分几个大标题对项目进行介绍,最简单的就是分为如下的4部分:

  • 项目名及简介:简单介绍一下这个项目是做什么的。有的话最好加上demo地址;
  • 功能:你这个项目可以实现的功能;
  • 用法:这可以说是最重要的,一定要让别人看得懂你这项目是怎么使用的;
  • 其他:作者或者是维护人列表、版权、鸣谢、贡献、logo、联系方式等等,这些有的话当然会更加高大上。

让面试官能在最短的时间内了解项目才是最重要的,所以简介要简短,三言二语就把项目要做的事儿交待清楚。这样面试官才会想更多的了解这个项目,例如使用了什么技术呀、具体源代码的书写逻辑呀等等。 

1.2 使用版本管理工具

最好使用版本管理工具,而且建议使用Git,并且是2个或多个人参与开发最好。许多面试官看到一个项目时通常都会问“这个项目是几个人开发的?”,除了了解项目分工的细节,也能体现团队合作的精神。另外在使用版本管理时,commit代码时一定要认真书写简短的描述,描述此次开发了什么功能或修复了什么Bug等,不能敷衍了事,这是一种习惯,会让自己以后受益。 

1.3 好的代码书写习惯

许多面试官司也会看具体的项目实现,所以项目模块划分要清晰,模块名称要见名之意,具体的代码书写要规范,现在许多大公司都有自己的代码规范,我们可以好好学一下阿里的Java代码规范。 

1.4 有完整的测试用例

这也是最容易被忽视的,但是却是保证项目功能正确最不可或缺的一部分。项目简单还好说,如果项目稍等复杂一点,没有测试用例保证的项目很容易出问题。试想一下,假如我们修复了一个系统Bug,但是没有为此Bug添加测试用例,那么下一个人在修复其它Bug时,很可能会破坏之前修复Bug的代码逻辑,导致一个Bug的修复引入了另外的Bug。所以要针对代码重要功能及相关Bug有完整的测试用例,必要时还要添加详细的注释。 

2 Javac AST View插件的开发  

下面博主打算写一个系列的博文,从零编写一个“规范”的开源项目,好在以后面试中更多的展现自己的实力。今天先简单介绍一下这个项目,并且为这个项目做一些准备工作。

2.1 项目介绍

开发一个类似Eclipse AST View的插件,安装的过程见如下博文: 

https://www.cnblogs.com/nettee/p/4463841.html

这个插件的详细使用说明的链接如下:

https://www.eclipse.org/jdt/ui/astview/

安装后就可以直观地查看抽象语法树了,举个例子,如下:

package com.compiler;
 
import java.util.List;
 
public class C {
  public void test(List<String> list){
    list.add("a");
  }
}

生成的抽象语法树如下:

不过这个插件显示的是基于Eclipse JDT中的增量式编译器ECJ的抽象语法树,而我们经常使用的OpenJDK中的Javac编译器的抽象语法树与ECJ的抽象语法树并不相同,不过绝大多数的语法树节点划分是一样的,但是有少量的节点划分不一样,我打算开发一个类似Eclipse AST View的插件,用来显示Javac编译器的抽象语法树。 

2.2 项目知识储备 

开发这样的插件需要了解JDT编译器和Javac编译器的抽象语法树,同时还需要掌握Eclipse下的插件开发,参考的相关资源如下:

第一本就是《Eclipse插件开发学习笔记》,开发Eclipse插件必须要有插件开发相关基础,这本书发版时间很早,但是插件开发的基本思想是不会变的。另外官方的Eclipse API也是开发中必不可少的资料。

有了插件开发基础后就需要了解OpenJDK的Javac编译器和Eclipse JDT中的增量式编译器ECJ了,重点就是了解这两个编译器对抽象语法树节点的表示。对Javac编译器来说,还需要了解从Java源代码解析为抽象语法树的过程,参考的资料为《深入解析Java编译器:源码剖析与实例详解》。

Eclipse JDT中的增量式编译器ECJ的抽象语法树可以通过博文Eclipse AST抽象语法树API来了解,没有相关的书籍。不过由于是在Eclipse中开发插件,所以直接调用Eclipse相关API来获取抽象语法树就可以了,不需要自已编写Java源代码转换为抽象语法树的代码实现。所以Eclipse AST View整个项目的实现也相对简单。 

把Eclipse AST View项目的源代码导入到Eclipse中,这样就可以通过阅读、调试的方式来学习这个插件了,然后参考这个插件来写我自己的Javac AST View插件。项目源代码结构如下:

不得不说,阅读别人的代码也是学习的最好方式之一。 

2.3 编写项目框架 

编写的插件中主要使用了树插件,JFace为树控件提供了查看器Viewer。在查看器框架中,将模型称为输入,查看器本身充当控制器的角色,而树控件本身作为视图,当输入改变时,查看器负责相应地改变控件的内容。

查看器框架主要由以下几部分构成。

(1)模型和元素-存储着要显示在控件中的数据模型,我们编写的抽象语法树节点模型如下:

package astview;
 
import java.util.ArrayList;
import java.util.List;
 
public class JavacASTNode {
    private int id;
    private String name;
    private List<JavacASTNode> children;
    private JavacASTNode parent = null;
 
    public JavacASTNode() {
        children = new ArrayList<JavacASTNode>();
    }
 
    public List<JavacASTNode> getChildren() {
        return children;
    }
 
    public void setChildren(List<JavacASTNode> children) {
        this.children = children;
    }
 
    public JavacASTNode getParent() {
        return parent;
    }
 
    public void setParent(JavacASTNode parent) {
        this.parent = parent;
    }
 
    public int getId() {
        return id;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String toString() {
        return name;
    }
}

(2)内容提供者和标签提供者-负责将数据模型转化成可以显示的图片和文字。我编写的内容提供者如下:

class ViewContentProvider extends ArrayContentProvider implements ITreeContentProvider {
 
    public Object[] getChildren(Object parentElement) {
        JavacASTNode node = (JavacASTNode) parentElement;
        return node.getChildren().toArray();
    }
 
    public Object getParent(Object element) {
        JavacASTNode node = (JavacASTNode) element;
        return node.getParent();
    }
 
    public boolean hasChildren(Object element) {
        JavacASTNode node = (JavacASTNode) element;
        return node.getChildren().size() > 0 ? true : false;
    }
 
    public Object[] getElements(Object inputElement) {
        JavacASTNode compilatinUnitNode = new JavacASTNode();
        compilatinUnitNode.setId(001);
        compilatinUnitNode.setName("JCCompilationUnit");
         
        JavacASTNode importNode = new JavacASTNode();
        importNode.setId(002);
        importNode.setName("JCImport");
         
        JavacASTNode classNode = new JavacASTNode();
        classNode.setId(003);
        classNode.setName("JCClassDecl");
 
        compilatinUnitNode.getChildren().add(importNode);
        compilatinUnitNode.getChildren().add(classNode);
        importNode.setParent(compilatinUnitNode);
        classNode.setParent(compilatinUnitNode);
         
        return new JavacASTNode[] {compilatinUnitNode};
    }
 
}

这个类是JavacTreeViewer的内部类。我们只简单写了一些测试用的数据,其实这些数据都是要从Javac编译器中读取的,而Javac编译器分析的Java源代码又需要从当前激活的编辑器中获取,后面我会不断完善更新这些功能。  

编写的标签提供者如下: 

class ViewLabelProvider extends LabelProvider {
    public Image getColumnImage(Object element) {
        return null;
    }
 
    public String getColumnText(Object element) {
        return ((JavacASTNode) element).toString();
    }
}

这个类也同样是JavacTreeViewer的内部类。  

(3)控件,用来显示内容,这里用到的为树控件TreeViewer

(4)查看器

模型和元素以及内容提供者和标签提供者需要自己编写,控件和查看器不需要开发者自己编写。

编写JavacTreeViewer类,如下: 

package astview;
 
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
 
 
public class JavacASTViewer extends ViewPart {
    public static final String ID = "javacastviewer";
     
    private TreeViewer viewer;
 
    public void createPartControl(Composite parent) {
        viewer = new TreeViewer(parent, SWT.SINGLE);
        viewer.setLabelProvider(new ViewLabelProvider());
        viewer.setContentProvider(new ViewContentProvider());
        viewer.setInput(getSite());
    }
 
    public void setFocus() {
        // not supported
    }
        ...
}

最后运行后,查看JavacTreeViewer视图,显示效果如下:

最后就是上传到github了,我的github仓库地址为:

https://github.com/mazhimazh/JavacASTViewer

接下来我会不断开发完善这个项目,如果各位能给个“Star”是最好了,你的支持是我继续开发的最大动力。

 

 

发布了167 篇原创文章 · 获赞 321 · 访问量 58万+

注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: