项目链接:92rw/ideFx: Use JavaFX to create simple IDE (github.com)

项目背景

我在学习 Java 语言的最初几节课,是通过记事本编写一个 .java 格式的文件,然后在命令行中通过 javac 指令编译成 .class 字节码文件,然后通过命令行的 java 指令运行得到效果。然后我接触到了 IntelliJ IDEA 这样的集成开发环境(IDE),大大提到了编码效率。当时对这种程序实现自动构建运行环境、简化编译和运行的原理十分感兴趣。

在学习 Java 图形化编程之后,我被最新的 JavaFX 技术所吸引。因为封装了很多基本的 GUI 组件和功能,可以更加关注于设计思路和具体的实现。因此我决定使用 JavaFX 技术模拟 IntelliJ IDEA 写一个项目,同时检验我的编码水平。

虽然我注册 Github 账号已经五年多,但是我之前对 Git 版本控制确实了解不深。参考其他项目的开发架构,我将最新的代码上传到 Dev 分支,测试稳定后 pull request 给 main 分支,防止开发版本的错误传递到正式版本。

实现功能

  • 初始界面可以对加载到 ideFx 程序中的项目文件夹进行集中管理
  • 支持对项目目录下的 .java 文件进行编译、运行
  • 支持对项目目录下的 .class 文件进行反编译
  • 进入在项目目录下的文件实现文件操作和文本编辑
  • 在文本编辑器中,对 java 语言的关键词高亮显示
  • 在控制台输出模块,用不同的颜色标注显示信息的类型
  • 除了用户在系统环境变量中配置的 JDK,还可以设置为其他版本的JDK用于测试
  • 用户可切换窗体的显示效果

项目架构

项目目录结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
.gitattributes
.gitignore
│ LICENSE
│ mvnw
│ mvnw.cmd
│ pom.xml
│ README.md

├─.idea
.gitignore
│ compiler.xml
│ encodings.xml
│ jarRepositories.xml
│ misc.xml
│ uiDesigner.xml
│ vcs.xml
│ workspace.xml

├─.mvn
│ └─wrapper
│ maven-wrapper.jar
│ maven-wrapper.properties

└─src
└─main
├─java
│ │ module-info.java
│ │
│ └─com
│ └─rwj
│ └─idefx
│ │ FontLoader.java
│ │ JavaSyntaxHighlighter.java
│ │ Launcher.java
│ │
│ ├─controller
│ │ ApplicationController.java
│ │ ProjectController.java
│ │ RuntimeController.java
│ │
│ ├─model
│ │ AppConfig.java
│ │ CustomizeModel.java
│ │ ExecutionResult.java
│ │ FileModel.java
│ │ OSType.java
│ │ ProjectConfig.java
│ │ ThemeInfo.java
│ │
│ └─view
│ ContextMenuBuilder.java
│ CreateProjectDialog.java
│ DialogView.java
│ MainView.java
│ ProjectListCell.java
│ StartView.java

└─resources
├─com
│ └─rwj
├─fonts
│ └─jetbrains
│ JetBrainsMono-Medium.ttf
│ JetBrainsMono-MediumItalic.ttf

└─themes
└─codearea
dracula.css
light.css
monokai.css

总结的经验

  1. 关注点分离

    • MVC 模式由哪一层处理用户需求?当显示给用户的信息都需要等待后端返回结果时,应当由 Controller 层做主导,返回 View 层的内容;当程序主要依赖 UI 组件的用户交互时,可以由 View 层做主导,Controller 层作为中间层。
    • MVVM 模式究竟意味着什么:ViewModel 作为中间层,虽然不能直接操作 View 层的组件,但是可以通过“绑定”来改变 View 层组件显示的信息。
  2. 编写 Java 程序时的感悟

    • 当代码过长时,将某一个功能相关的代码整体写入一个方法体,以便通过方法的说明了解实现了哪些内容,
    • 使用建造者模式封装需要传入的参数,方便后期调整时的代码阅读。
    • “DTO”类作为数据传输的类,在普通的数据传递时也可以使用:编译异常除了可以通过转为运行时异常,也可以用一个“DTO”类捕获;程序正常运行时的结果,除了直接输出字符串,也可以用“DTO”类传递。
  3. 虽然在 IntelliJ IDEA 新建的 JavaFX项目默认将组件的信息和调用的方法写在自动生成的 .xml 文件中,可以通过 SceneBuilder 可视化地设定布局效果,然而这种方式并不完美

    • SceneBuilder 的很多组件都只有默认样式,如果要追求更好的显示效果,仍需要在布局格局效果后自行设计
    • 虽然 UI组件 的配置单独提取出来,但是 Controller 层将用户交互、调用后端的代码都进行集中管理,违背了单一职责原则
  4. 加深对数据结构的理解

    • “文件树” 对应的树结构,通过递归访问对应目录获取节点。为了防止文件夹深度过高造成的访问速度受影响,需要通过“递归深度”这个概念来进行控制。
    • 列表中的元素,可以包含多个不同的属性参数。在UI组件调用时,可以给不同属性指定各自的显示效果。这种对象类似于前后端交互时的“分页”对象
  5. 对项目结构的认识

    • 在项目设计时,考虑到项目结构本身较为简单,引入过多的依赖会造成结构臃肿、拖慢运行时长。因此在 View 层通过调用单例类静态方法的方式操作 Controller 层,没有通过 Spring 框架击中管理。
    • 在实际的代码编写中,通过自己的方式实现了依赖注入(在 StartView 显示 UI 界面前,先加载项目初始化信息)。只是没有像 Spring 框架那样把 Controller 层接口化,仍然通过调用具体类的方式实现。

吸取的教训

  1. 软件开发应当在设计、架构上投入更多的时间,代码只是实现设计的方式。前期设计不周全会导致后期大面积的修改调试。
  2. 应当提前考虑变量之间的关系。当UI组件之间有一些相互操作的代码逻辑之后,要想将一些功能另外提取到新的类已经变得十分困难,增加新功能只会让现有代码变得更复杂难以阅读。
  3. Java 新版本要求导入的包都实现模块化,否则无法顺利打包。因此在引入新的依赖时要注意和当前环境变量是否匹配。
  4. 随着项目变大,对某个业务流程进行“微调”越来越难,很多时间被用于整理过去写过的代码。因此及时写项目文档很重要,能帮助快速定位到需要检查的方法。