把书读薄 _ 《设计模式之美》规范与重构,学习Android开发的步骤


0x1、重构四问

① 重构的目的 → 为什么重构(Why)?

软件设计大师Martin Fowler对重构的定义:

重构是一种对软件内部结构的改善,目的是 在不改变软件的可见行为 的情况下,使其更易理解,修改成本更低。

可以把重构理解为:

在保持功能不变的前提下,利用设计思想、原则、模式、编程规范等理论来优化代码,修改设计上的不足,提高代码质量。

为什么要进行代码重构

  • 时刻保证代码质量的有效手段,不至于让代码腐化到无可救药的地步;
  • 优秀的代码或架构都是迭代出来的,无法100%预见未来的需求,随着系统演进,重构不可避免;
  • 避免过度设计的有效手段,维护代码过程真正遇到问题再对代码进行重构,有效避免前期过度设计投入大量时间;
  • 对工程师本身技术的成长有重要意义,将学习到的理论知识应用到实践中一个很好的练兵场;

初级工程师在维护代码,高级工程师在设计代码,资深工程师在重构代码 (发现代码存在问题,保证代码质量处于可控的状态)。


② 重构的对象 → 重构什么(What)?

根据重构的规模,笼统地分为 大规模高层次重构小规模低层次重构,简称为大型、小型重构。

大型重构

对顶层代码设计的重构系统、模块、代码结构、类与类之间的关系 等的重构,手段有:分层、模块化、解耦、抽象可复用组件 等。工具:设计思想、原则和模式。涉及代码变动较多,影响面大,难度大,耗时长,引入bug风险也大。

小型重构

对代码细节的重构类、函数、变量等代码级别 的重构,比如:规范命名、规范注释、消除超大类或函数、提取重复代码等。手段主要是:编码规范。修改地方较集中,比较简单、可操作性强、耗时短,引入bug风险也相对小一些。


③ 重构的时机 → 什么时候重构(When)?

不要等代码烂到一定程度再去重构,提倡 持续重构,闲暇时看下项目中有哪些写的不够好、可以优化的代码,主动去重构下,或者在修改、添加某功能代码时顺手把不符合编码规范、不好的设计重构一下,就是要有 持续重构的意识


④ 重构的方法 → 如何重构(How)?

大型重构

提前做好完善的重构计划,分阶段进行,每个阶段只完成一小部分代码的重构,然后提交、测试、运行,发现没问题后,再继续进行下一阶段的重构,保证代码仓库中的代码一直处于可运行,逻辑正确的状态。

每个阶段,都要控制好重构影响到的代码范围,考虑好如何兼容老的代码逻辑,必要的时候还要写一些兼容过渡代码。只有这样,才能让每个阶段的重构不至于耗时太长(最好一天就能完成),不至于和新的功能开发相冲突。

大型重构一定是有组织、有计划且非常谨慎的,需要有经验、熟悉业务的资深同事来主导。

小型重构

随时可以去做,除了人工去发现低层次的代码质量问题,还可以借助一些成熟的静态代码分析工具(如:CheckStyle、FindBugs、PMD等),来自动发现代码中的问题,然后针对性地进行重构优化。

对于重构这件事,资深工程师、团队Leader要负起责任,没事就重构下代码,时刻保持代码质量处于一个良好的状态。否则一旦出现 “破窗效应”,一个人往里堆了一些烂代码,之后就会有更多的人往里面堆更烂的代码,毕竟往项目堆砌烂代码的成本太低了。保持代码质量最好的方法还是:打造一种好的技术氛围,以此驱动大家主动关注代码质量,持续重构代码。


0x2、如何保证重构不出错

保证重构不出错,你需要熟练掌握各种设计原则、思想、模式,还有对所重构的业务和代码有足够的了解。除去这些个人能力因素外,最可落地执行、最有效的保证重构不出错的技术手段就是 单元测试(Unit Testing)。

① 单元测试与集成测试

  • 单元测试由研发工程师自行编写,用来测试自己写的代码的正确性,测试对象是 类或函数,测试是否都按照预期的逻辑执行,代码层级的测试,粒度较小;
  • 集成测试 (Integration Testing) 的测试对象是 整个系统或某个功能模块,如测试用户注册、登陆功能是否正常,一种端到端(End To End) 的测试。

② 单元测试编写示例

import java.util.regex.Pattern;

public class Text {
private String content;
private final Pattern pattern = Pattern.compile(“[0-9]*”);

public Text(String content) {
this.content = content;
}

public Integer toInt() {
if (content == null || content.isEmpty()) return null;
String temp = content.replace(" ", “”);
if(pattern.matcher(temp).matches()) {
return Integer.parseInt(temp);
}
return null;
}
}

比如要对上面这个Text类的toInt()方法进行测试,先设计测试用例(输入 → 期望输出):

  • “123” → 123
  • null或空字符串 → null
  • " 123"、" 123 "、"123 "、"1 23 "、"1 2 3 “、” 1 2 3 " → 123
  • “123a”、“1*23”、“abc” → null
  • “1234567890” → 1234567890

用例设计更多考验程序员思维的缜密程度,看能否设计出覆盖各种正常/异常情况的测试用例,来保证代码在任何预期或非预期情况下都能正确运行。写完用例,接着就是将其翻译成带么了(此处没用任何测试框架)

// 结果校验类
public class Assert {
public static void assertEquals(Integer expectedValue, Integer actualValue) {
if (actualValue.intValue() != expectedValue.intValue()) {
System.out.println(String.format(“测试失败:期待值:%d,实际值: %d”, expectedValue, actualValue));
} else {
System.out.println(“测试成功”);
}
}

public static boolean assertNull(Integer actualValue) {
boolean isNull = actualValue == null;
if (isNull) {
System.out.println(“测试成功”);
} else {
System.out.println(“测试失败,实际值不为null:” + actualValue);
}
return isNull;
}
}

// 测试用例类
public class TextTest {
public void testToNumber() {
Assert.assertEquals(123, new Text(“123”).toInt());
}

public void testToNumber_nullOrEmpty() {
Assert.assertNull(null);
Assert.assertNull(new Text(“”).toInt());
}

public void testToNumber_containsSpace() {
Assert.assertEquals(123, new Text(" 123").toInt());
Assert.assertEquals(123, new Text(" 123 ").toInt());
Assert.assertEquals(123, new Text("123 ").toInt());
Assert.assertEquals(123, new Text("1 23 ").toInt());
Assert.assertEquals(123, new Text("1 2 3 “).toInt());
Assert.assertEquals(123, new Text(” 1 2 3 ").toInt());
}

public void testToNumber_containsInvalidCharacters() {
Assert.assertNull(new Text(“123a”).toInt());
Assert.assertNull(new Text(“1*23”).toInt());
Assert.assertNull(new Text(“abc”).toInt());
}

public void testToNumber_large() {
Assert.assertEquals(Integer.MAX_VALUE, new Text(“” + Integer.MAX_VALUE).toInt());
}
}

// 运行用例类
public class TestCaseRunner {
public static void main(String[] args) {
TextTest test = new TextTest();
test.testToNumber();
test.testToNumber_nullOrEmpty();
test.testToNumber_containsSpace();
test.testToNumber_containsInvalidCharacters();
test.testToNumber_large();
}
}

测试结果如下:

从上面的示例我们可以总结出:写单元测试就是针对代码设计覆盖各种输入、异常、边界条件的测试用例,用将用例翻译成代码的过程。

另外,翻译代码时,可利用单元测试框架(如JUnit、TESTNGINX、Spring Test等) 来简化测试代码的额编写。


③ 为什么要写单元测试

  • 帮你发现代码中的BUG (节省fix低级bug的时间,写出Bug Free代码是判断工程师编码能力的重要标准之一);
  • 帮你发现代码设计上的问题 (代码的可测试性是评判代码质量的重要标准,难写单元测试一般说明代码设计可能有问题);
  • 单元测试是对集成测试的有力补充 (复杂系统,集成测试也无法覆盖得很全面);
  • 写单元测试的过程本身就是代码重构的过程
  • 阅读单元测试能帮助你快速熟悉代码(单元测试案例实际上就是用户案例,反映了代码的功能及使用,在没有文档或注释的情况下,它可以起替代性作用,借助单元测试案例,无需深入阅读代码,即可了解代码实现了什么功能);
  • 单元测试是TDD可落地执行的改进方案 (Test Driven Development,测试驱动开发,测试用例优于代码编写);

④ 编写单元测试的经验总结

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!

在这里我也分享一份自己收录整理的**Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料**帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

.(img-JfM0swGb-1711016841963)]
[外链图片转存中…(img-NT548ng8-1711016841964)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-KCbK4Hyn-1711016841964)]

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值