Blog | Phodal - A Growth Engineerhttp://www.phodal.com/blog/2020-01-09T14:52:02.542962+00:00Blog如何做遗留系统的重构评估2020-01-09T14:50:08+00:002020-01-09T14:52:02.542962+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/evaluate-legacy-system/## 重构评估与度量
在接触代码之前,我们可以通过一些现成的工具,来对现有的项目进行一些不评估,并通过度量来提供指标。
### 识别技术债务
> 对于技术债务,它的利息表现为系统的不稳定性,以及由于临时性手段和缺乏合适的设计、文档工作和测试带来的不断攀升的维护成本。 —— 《架构师应该知道的 97 件事》
如 Robert Nord 提出的 “技术债务全景图”(Tech Debt Landscape) 所示:
![技术债全景图](https://migration.ink/images/tech-debt-landscape.png)
技术债对于软件的影响:可维护性(Maintainability)、可演进性(Evolvability),而这些技术债对于非技术人员来说都是不可见的。它们源于生活,藏于黑暗中。
#### 技术债风暴
在重构开始之前,我们可以进行技术债的头脑风暴,收集每个开发人员每迫切解决的技术痛点。并按照优先级来评估这些技术债,列入我们的重构范围中。
如我的同事在《[技术债治理的四条原则](https://insights.thoughtworks.cn/managing-technical-debt/)》 一文中所介绍的,我们可以在对应的限界上下文里,可视化技术债:
![Classified Technical Debt Mapping](https://migration.ink/images/tech-debt-mapping.png)
再根据 “核心领域优于其他子域” 的原则,及其严重程度,来划分出技术债的优先级。
#### 架构评估:技术驱动 vs 业务驱动
如我在那篇 《[分层架构重构](https://www.phodal.com/blog/refactor-mvc-architecture-to-ddd/)》 中所说,在大量的现有系统中,我们发现了 MVC 架构模式被落地为三层分层架构(controller-service-model)。开发人员对它们的错误等同,导致了架构上的一系列错误。
对于简单的系统来说,CSM 的包结构问题不大。或者说,对于非常简单的系统来说,大泥球架构也没有问题。我们所针对的是那些中大规模的系统。在这些系统里,系统并非一次性的,开发出来就不再维护了。因此,它们需要对更合适的架构设计和包的拆分分。
借助于 [Tequila](https://github.com/newlee/tequila) 这样的架构可视化工具,又或者是 `coca arch`,便可以得到项目的调用关系图,它可以在某种层面上反应出系统的架构。根据它,我们可以知道:
- 项目的结构划分是否合理
- 查看项目的代码中是否存在循环依赖的情况
结果如下图所示:
![Coca Call Graph](https://migration.ink/images/coca-call.png)
通过调用关系图,我们也可以查看类之间、包之间是否存在相互依赖。
#### 代码评估:收集 bad smell
对于这部分内容来说,你可以直接采用成熟的商业工具,如 SonarQube 便可以完成这方面的工作。
你也可以通过 `coca bs` 来做一些简单的 Bad Smell 收集:
```json
{
"dataClass": [
{
"File": "examples/api/BookController.java",
"BS": "dataClass"
}
],
"lazyElement": [
{
"File": "examples/api/model/BookRepresentaion.java",
"BS": "lazyElement"
}
]
}
```
而后,再生成对应的重构建议。
#### 收集 Todo
代码中的 Todo 注释,是一些本应该发生的事情,本应该做好,但是我没有立即去做。换句话来说,Todo 都是项目中的技术债务,就了可能就永远不会做。
所以,我们需要有工具来查找项目的 Todo,如笔者编写的 Coca,可以寻找代码中的 Todo,包含其对应的日期、作者、提交信息、文件名及对其的行数等信息:
| MESSAGES | FILENAME | LINE |
|--------------------------------|--------------------------------------------------------------------------------------|------|
happens on macosx, don't know why | .../ContributedLibraryTableCellJPanel.java | 118
Make this a method of Theme | .../ContributedLibraryTableCellJPanel.java | 233
Do a better job in refreshing only the needed element | .../LibraryManagerUI.java | 241
Do a better job in refreshing only the needed element | .../LibraryManagerUI.java | 273
Make this a method of Theme |.../MultiLibraryInstallDialog.java | 149
happens on macosx, don't know why | .../ContributedPlatformTableCellJPanel.java | 183
show error error when importing. ignoring :( | .../Base.java | 2423
Improve / move error handling |.../Editor.java | 1541
Should be a Theme value? |.../EditorHeader.java | 78
Should be a Theme value? |.../EditorStatus.java | 73
Improve decoupling | .../EditorTab.java | 465
随后,我们只需要根据真实的情况,更新项目中的 Todo,以确认出我们需要完成的技术债务。
不过,写好一个 Todo 并不是容易,万一以后大家都不写了呢?
#### 测试和文档评估
关于测试的话题,我们会有一个大的专题来介绍相关的活动。
至于文档的缺乏,会在文中的最后介绍。
不过,你也可以参考我的那篇《[构建质量可信的软件系统](https://www.phodal.com/blog/build-trusted-software-system/)》 来对你的文档进行评估。
### 项目评估
根据不同的项目,侧重点有所不同。
但是毫无疑问地,我们可以统计:
- 功能的 bug 率,对应的 bug 修改时间
- bug 常见的问题
- ……
你都懂的。我暂时就不 copy 了。
### 编写工具评估
在我遇到的一个重构项目中,项目中经常抛出 Null Point Exception 的问题。于是,我便写了一个简单的工具,来查找项目中返回 null point exception 的代码,并对调用的地方进行评估。
随着评估的进一步深入,我在工具中加入了更多的功能,如:
- 静态方法多,难以进行测试。要么是工具类过多,需要抽取基础设施;要么就是缺乏 OO 设计,导致过程性代码……。
- Util 过多,同上。
- Null Point Exeception 越多,则项目的出错可能性越多。
- 类方法数的标准差,能判断出对应的上帝类情况。
- 方法长度的标准差,越大则意味着方法的长度都比较长,方便于重构。
只需要运行 `coca evaluate`,就能得到以下的结果:
| TYPE | COUNT | LEVEL | TOTAL | RATE |
|--------------------------------|-------|-----------------------|-------|-----------|
| Nullable / Return Null | 0 | Method | 1615 | 0.00% |
| Utils | 7 | Class | 252 | 2.78% |
| Static Method | 0 | Method | 1615 | 0.43% |
| Average Method Num. | 1615 | Method/Class | 252 | 6.408730 |
| Method Num. Std Dev / 标准差 | 1615 | Class | - | 7.344917 |
| Average Method Length | 13654 | Without Getter/Setter | 1100 | 12.412727 |
| Method Length Std Dev / 标准差 | 1615 | Method | - | 20.047092 |
笑,你只要加强使用 TDD,那么上述的大部分问题,都能得到进一步的缓解。
### 代码评估工具
Java 世界流行的几个找问题工具:
- FindBugs
- PMD
- Checkstyle
试试你就知道了。
### 真实的测试覆盖率
尽管有越来越多的项目将测试覆盖率作为一项考核指标。但是,对于诸多编程实践本身就好的公司为说,测试覆盖率也往往不是真的。
我们编写测试的其中一个目的是用于快速反馈,即当我们的功能出现问题的时候,我们可以快速通过测试来定位到问题所。然而,如果那些是没有断言的测试,那么我们就无法通过它来进行快速反馈。即,如果我们重构过程中,修改了某一块的功能,可能会进一步导致出现 bug。
为此,你可以借助于 Coca 的 Test Bas Smell 功能,来找到对应的问题。只需要执行 `coca tbs`,便能帮助你找到代码中的坏味道。它可以在你进入重构之前,帮你看看是否有对应的风险。
如下是 Coca 扫描出来的 Arduino 开源项目测试问题:
| TYPE | FILENAME | LINE |
|---------------------|---------------------------------------------------------------|------|
| DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 107 |
| DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 41 |
| DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 63 |
| RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 71 |
| RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 72 |
| RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 77 |
| DuplicateAssertTest | app/test/cc/arduino/net/PACSupportMethodsTest.java | 19 |
| DuplicateAssertTest | app/test/processing/app/macosx/SystemProfilerParserTest.java | 51 |
| DuplicateAssertTest | app/test/processing/app/syntax/PdeKeywordsTest.java | 41 |
| DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 57 |
| DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 83 |
| DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 109 |
好在上述的测试代码中,没有出现诸如于下面场景的测试坏味道:
- EmptyTest。测试函数里空空如也
- UnknownTest。测试中没有对应的断言
- IgnoreTest。测试是被 Ingore 的,即不会运行的测试。
如果你的代码中出现了大量的上述问题,你需要好好反思一下,你的测试覆盖率是真实的吗?
### 可测试性评估
代码本身是缺乏测试的,那么它就是一个遗留系统。
### 度量
根据《精益软件度量》对于度量的定义:
- 度量在组织上下文中形成的一系列共识
- 将经验性模型转换为向量化模型(修改)
- 包含人、流程、组织和工具的一个动态系统
TBD
### 寻找专业人士
你懂的。
## 结论
已合入:[https://migration.ink/](https://migration.ink/)测试代码的坏味道2020-01-06T10:00:00+00:002020-01-06T07:52:29.599104+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/test-bad-smell/测试代码才能真正体现开发人员的水平。
> 追求技术卓越是采用敏捷的第一成功要素。 —— Jeff Sutherland 敏捷宣言创始人之一
- Phodal: “你为什么写测试?”
- 开发人员 A:“为了测试覆盖率”。
- Phodal: “咦,这个测试没有断言”
- 开发人员 A 笑了笑。
某次代码重构中,我发现代码的测试覆盖率很高,过程中出了一些错误,重构手法不正确是一个问题。但是在重构的过程中,发现有些测试都是没有意义的,所以我变转向开始研究测试坏味道,顺便在 Coca 中写了个识别代码测试坏味道的工具。
## 测试反应开发人员的水平
与编写业务代码相比,测试代码才能真正体现开发人员的水平。你可以用测试来判断开发人员的水平:
- 有没有为自己的代码编写测试?
- 测试中有没有断言?
- 测试中有没有包含有效的断言?
- 测试的长度是否正常?
- 测试中的断言是否合理?
没有断言的测试意味着原本的代码写得又臭又长;测试中只包含无效断言表明开发人员在划水;测试方法的长度过长,表明原有的方法可以进一步抽象……
顺便一提,我们推荐的 TDD(测试驱动开发),它并非是银弹。但是,当你来面对一个复杂的场景时,它可以驱动出可测试的代码,辅助以重构,能帮助你写出短小的函数。借此整体上降低整一部分代码的开发 + 维护成本。
我知道你想说有人的很聪明,可以写出的代码足够的健壮。但是呢,这样的人存在吗?即使存在的话,需求是善变的,下一次接手代码的人能保证原有的功能是好的吗?
### 正视测试同正视 bug 一样
> 软件测试(英语:Software Testing),描述一种用来促进鉴定软件的正确性、完整性、安全性和质量的过程。换句话说,软件测试是一种实际输出与预期输出间的审核或者比较过程。
项目代码是日常接触最多的部门,我们会直面代码中的问题,也因此会重视它们。对于测试嘛,就呵呵了。但是,你们就这么忽略了测试的重要性。
我们编写测试是为了提升软件开发质量,一旦代码改出了问题,那么测试就会帮我们找出破坏了的原有功能。而不是在长长的软件测试反馈链之后,才发现:原来我们改出了 bug。
不过呢,当你的业务进度压力大的时候,没有时间编写测试,反而 bug 就更多了。
## 测试代码坏味道
> 代码坏味道是对应于系统中的更深层问题的表面指示。
我们一般谈论代码坏味道的时候,主体是项目代码,而测试代码坏味道则往往被人忽略了。测试代码能直观地反应出代码的设计问题,它们是 API 的使用方,它们是 API 的第一等使用方。
> 测试代码坏味道,是指单元测试代码中的不良编程实践(例如,测试用例的组织方式,实现方式以及彼此之间的交互方式),它们表明测试源代码中潜在的设计问题。
如 Robert C. Martin 在《代码整洁之道》所说的那样,好的测试应该是:
- 快速(Fast),测试应该够快。
- 独立(Indendent),测试应该相互独立。
- 可重复(Repeatable),测试应当可在任何环境中通过。
- 自足验证(Self-Validating),测试应该有布尔值输出。
- 及时(Timely),测试应该及时编写。
要我说的话,它应该还有:
- **同一人编写**,测试应该由开发业务代码的编写。这样他/他们才知道自己代码写得烂。
- **边界**,测试直接不影响业务代码。这里指的主要是 private -> public 的行为,又或者是业务代码中包含测试代码,而非因为测试对原有代码重构。
- **有效命名**。测试信息应该体现在方法名上,表达某一个特定的需求。
测试代码应该遵循生产代码的质量标准。
命名在测试中也是一大难题,我们如可以采用 Roy Osherove(《单元测试的艺术》作者) 推荐的 `UnitOfWork_StateUnderTest_ExpectedBehavior` [命名法则](https://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html)。 几个示例如下:
```
Public void Sum_NegativeNumberAs1stParam_ExceptionThrown()
Public void Sum_NegativeNumberAs2ndParam_ExceptionThrown ()
Public void Sum_simpleValues_Calculated ()
```
## 测试代码坏味道示例
先让我们来看看有哪些常见的测试坏味道:
- 空的测试。测试是生成的,但是没有内容。
- 忽略的测试。即测试被 Ignore
- 没有断言的测试。为了测试覆盖率而出现的测试
- 多余的 Println。调试时留下的讯息。
- 多重断言。每个测试函数只应该测试一个概念。
- ……。
然后,再来个 Examples。
这是 Arduino 代码中的 `I18NTest.java` 文件,先看看文件,再看看问题:
```
@Test
public void testAllLocales() {
for (Language language : Languages.languages) {
if (!language.getIsoCode().equals("")) {
Locale locale = toLocale(language);
ResourceBundle bundle = ResourceBundle.getBundle("processing.app.i18n.Resources", locale);
if (locale.equals(bundle.getLocale())) {
Collections.list(bundle.getKeys()).stream().map(bundle::getString).filter(key -> !key.contains("<html")).foreach(key -=""> {
try {
I18n.format(key);
} catch (IllegalArgumentException e) {
System.out.println(language);
System.out.println(key);
throw e;
}
});
} else {
System.out.println("Missing locale: " + locale);
}
}
}
}
```
问题有很多:
- 没有断言
- 多余的 print 函数
- try...catch...
- 糟糕的测试命名
这个测试的正确作法之一应该是:使用容器 collection 来过滤出正确的语言,最后对比长度是否正确。
再举个例子:
```
@Test
public void testXmlSanitizer() {
boolean valid = XmlSanitizer.isValid("Fritzbox");
assertEquals("Fritzbox is valid", true, valid);
System.out.println("Pure ASCII test - passed");
valid = XmlSanitizer.isValid("Fritz Box");
assertEquals("Spaces are valid", true, valid);
System.out.println("Spaces test - passed");
...
}
```
这个测试用例违反了每个测试一个用例的原则。
## 坏味道检测工具
欢迎成为 Coca 的忠实用户,只需要运行 `coca tbs`,就可以识别出你的 Java 代码中的测试味道。如下是 Arduino 源码中的测试坏味道:
| TYPE | FILENAME | LINE |
|---------------------|---------------------------------------------------------------|------|
| DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 107 |
| DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 41 |
| DuplicateAssertTest | app/test/cc/arduino/i18n/ExternalProcessOutputParserTest.java | 63 |
| RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 71 |
| RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 72 |
| RedundantPrintTest | app/test/cc/arduino/i18n/I18NTest.java | 77 |
| DuplicateAssertTest | app/test/cc/arduino/net/PACSupportMethodsTest.java | 19 |
| DuplicateAssertTest | app/test/processing/app/macosx/SystemProfilerParserTest.java | 51 |
| DuplicateAssertTest | app/test/processing/app/syntax/PdeKeywordsTest.java | 41 |
| DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 57 |
| DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 83 |
| DuplicateAssertTest | app/test/processing/app/tools/ZipDeflaterTest.java | 109 |
Coca 开源,不要钱,不要钱。对 Coca Pro 有兴趣的,可以和我们联系,哈哈哈哈。
## 结论
回到开头:《敏捷宣言》上的原文是,『坚持不懈地追求技术卓越和良好设计,敏捷能力由此增强』。只是对于工具、手法和模式的理解,何时去使用各种各样的技术,以及考虑产品、需求的意图,大抵就是技术卓越的体现。
相关资料:https://testsmells.github.io/index.html ,我从这个网站上获得了 Coca 项目所需要的大量用例代码。</html")).foreach(key>一份代码构建移动、桌面、Web全平台应用2016-01-26T14:24:10+00:002016-01-30T14:24:29.176644+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/build-full-platform-application/Web本身就是跨平台的,这意味着这中间存在着无限的可能性。
我是一名Web Developer,对于我来能用Web开发的事情就用Web来完成就好了——不需要编译,不需要等它编译完。我想到哪我就可以写到哪,我改到哪我就可以发生哪发生了变化。
最近我在写Growth——一个帮助开发人员成长的应用,在近一个月的业余时间里,完成了这个应用的:
- 移动应用版:Android、Windows Phone、iOS(等账号和上线)
- Web版
- 桌面版:Mac OS、Windows、GNU/Linux
截图合并如下:
![growth-full-platforms.png](/static/media/uploads/growth-full-platforms.jpg)
而更重要的是它们使用了同一份代码——除了对特定设备进行一些处理就没有其他修改。相信全栈的你已经看出来了:
Web = Chrome + Angular.js + Ionic
Desktop = Electron + Angular.js + Ionic
Mobile = Cordova + Angular.js + Ionic
除了前面的WebView不一样,后面都是Angular.js + Ionic。
##从Web到混合应用,再到桌面应用
在最打开的时候它只是一个单纯的混合应用,我想总结一下我的学习经验,分享一下学习的心得,如:
- 完整的Web开发,运维,部署,维护介绍
- 如何写好代码——重构、测试、模式
- 遗留代码、遗留系统的形成
- 不同阶段所需的技能
- 书籍推荐
- 技术栈推荐
- Web应用解决方案
接着我用Ionic创建了这个应用,这是一个再普通不过的过程。在这个过程里,我一直使用Chrome在调度我的代码。因为我是Android用户,我有Google Play的账号,便发布了Android版本。这时候遇到了一个问题,我并没有Apple Developer账号(现在在申请ing。。),而主要的用户对象程序员,这是一群**不土**的土豪。
![iPHONE](/static/media/uploads/iphone.jpg)
偶然间我才想到,我只要上传Web版本的代码就可以暂时性实现这个需求了。接着找了个AWS S3的插件,直接上传到了AWS S3上托管成静态文件服务。
几天前在Github上收到一个issue——关于创造桌面版, 我便想着这也是可能的,我只需要写一个启动脚本和编译脚本即可。
所以,最后我们的流程图就如下所示:
![Growth Arch](/static/media/uploads/growth-arch.png)
除了显示到VR设备上,好像什么也不缺了。并且在我之前的文章《[Oculus + Node.js + Three.js 打造VR世界](https://github.com/phodal/oculus-nodejs-threejs-example)》,也展示了Web在VR世界的可能性。
在这实现期间有几个点可以分享一下:
1. 响应式设计
2. 平台/设备特定代码
##响应式设计
响应式设计可以主要依赖于Media Query,而响应式设计主要要追随的一点是不同的设备不同的显示,如:
![full-platforms.jpg](/static/media/uploads/full-platforms.jpg)
这也意味着,我们需要对不同的设备进行一些处理,如在大的屏幕下,我们需要展示菜单:
![gnu-linux.png](/static/media/uploads/gnu-linux.jpg)
而这可以依赖于Ionic的**expose-aside-when="large"**,而并非所有的情形都是这么简单的。如我最近遇到的问题就是图片缩放的问题,之前的图片针对的都是手机版——经过了一定的缩放。
这时在桌面应用上就会出现问题,就需要限定大小等等。
而这个问题相比于平台特定问题则更容易解决。
##平台特定代码
对于特定平台才有的问题就不是一件容易解决的事,分享一下:
###存储
我遇到的第一个问题是**数据存储**的问题。最开始的时候,我只需要开始混合应用。因此我可以用**Preferences**、或者**SQLite**来存储数据。
后来,我扩展到了Web版,我只好用LocalStoarge。于是,我就开始抽象出一个**$storageServices**来做相应的事。接着遇到一系列的问题,我舍弃了原有的方案,直接使用LocalStoarge。
###数据分析
为了开发方便,我使用Google Analytics来分析用户的行为——毕竟数据对我来说也不是特别重要,只要可以看到有人使用就可以了。
这时候遇到的一个问题是,我不需要记录Web用户的行为,但是我希望可以看到有这样的请求发出。于是对于Web用户来说,只需要:
```js
trackView: function (view) {
console.log(view);
}
```
而对于手机用户则是:
```js
trackView: function (view) {
$window.analytics.startTrackerWithId('UA-71907748-1');
$window.analytics.trackView(view)
}
```
这样在我调试的时候我只需要打个Log,在产品环境时就会Track。
###更新
同样的,对于Android用户来说,他们可以选择自行下载更新,所以我需要针对Android用户有一个自动更新:
```
var isAndroid = ionic.Platform.isAndroid();
if(isAndroid) {
$updateServices.check('main');
}
```
###桌面应用
对于桌面应用来说也会有类似的问题,我遇到的第一个问题是Electron默认开启了AMD。于是,直接删之:
```html
<script>
//remove module for electron
if(typeof module !== 'undefined' && module && module.exports){
delete module;
}
</script>
```
类似的问题还有许多,不过由于应用内容的限制,这些问题就没有那么严重了。
如果有一天,我有钱开放这个应用的应用号,那么我就会再次献上这个图:
![六边形架构](/static/media/uploads/hexoarch.png)
##未来
我就开始思索这个问题,未来的趋势是合并到一起,而这一个趋势在现在就已经是完成时了。
那么未来呢?你觉得会是怎样的?Intellij IDEA 重构——提炼函数2014-07-14T12:53:27+00:002015-08-20T03:50:56.236548+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/intellij-idea-refactor-extract-method/Intellij IDEA带了一些有意思的快捷键,或者说自己之前不在意这些快捷键的存在。重构作为单独的一个菜单,显然也突显了其功能的重要性,说说**提炼函数**,或者说提出方法。
##重构之提炼函数
快捷键
Mac: ``alt``+``command``+``M``
Windows/Linux: ``Ctrl``+``Alt``+``M``
鼠标: Refactor | Extract | Method
###重构之前
以重构一书代码为例,重构之前的代码
public class extract {
private String _name;
void printOwing(double amount){
printBanner();
System.out.println("name:" + _name);
System.out.println("amount" + amount);
}
private void printBanner() {
}
}
###重构
选中
System.out.println("name:" + _name);
System.out.println("amount" + amount);
按下上述的快捷键,会弹出下面的对话框
![Extrct Method][1]
输入
printDetails
那么重构就完成了。
###重构之后
IDE就可以将方法提出来
public class extract {
private String _name;
void printOwing(double amount){
printBanner();
printDetails(amount);
}
private void printDetails(double amount) {
System.out.println("name:" + _name);
System.out.println("amount" + amount);
}
private void printBanner() {
}
}
##Intellij IDEA重构
还有一种就以Intellij IDEA的示例为例,这像是在说其的智能。
public class extract {
public void method() {
int one = 1;
int two = 2;
int three = one + two;
int four = one + three;
}
}
只是这次要选中的只有一行,
int three = one + two;
以便于其的智能,它便很愉快地告诉你它又找到了一个飞盘
IDE has detected 1 code fragments in this file that can be replaced with a call to extracted method...
便返回了这样一个结果
public class extract {
public void method() {
int one = 1;
int two = 2;
int three = add(one, two);
int four = add(one, three);
}
private int add(int one, int two) {
return one + two;
}
}
然而我们就可以很愉快地继续和它玩耍了。当然这其中还会有一些更复杂的情形,当学会了这一个剩下的也不难了。
###代码
[https://github.com/gmszone/refactor](https://github.com/gmszone/refactor)
[1]: /static/media/uploads/extract-method.png