[解锁新姿势] 兄dei,你代码需要优化了
source link: http://mp.weixin.qq.com/s?__biz=MzU2NjIzNDk5NQ%3D%3D&%3Bmid=2247492201&%3Bidx=1&%3Bsn=e92a68218221fdf862e4792776a2b06d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
点击上方 “ 匠心零度 ” ,选择“ 设为星标 ”
做积极的人,而不是积极废人
来源:https://juejin.im/post/6844903983744548877
黑客(程序员)也是创作者,与画家、建筑师、作家一样。 ——《黑客与画家》
前言
在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题。甚至有些复制粘贴过来,不加以整理规范。往往导致项目后期难以维护,更别说后续接手项目的人。所以啊,我们要编写出优雅的代码,方便你我他,岂不美哉?
下面分享一些我在开发中常用的编码中 小建议
,如有不妥,欢迎大家一起交流学习。
卫语句
卫语句,就是把复杂的条件表达式拆分成多个条件表达式。比如 多个 if-elseif-else
嵌套, 可以拆分成多个 if
。如下面代码
代码:
-------------------- before --------------------
public void today() {
if (isWeekend()) {
if (isFee()) {
System.out.println("study Android");
} else {
System.out.println("play a game");
}
} else {
System.out.println("go to work");
}
}
-------------------- after (建议) --------------------
public void today() {
// 提前过滤掉`特殊情况`
if (!isWeekend()) {
System.out.println("go to work");
return; // 提前return
}
//提前过滤掉`特殊情况`
if (isFee()) {
System.out.println("study Android");
return; // 提前return
}
// 更关注于 `核心业务`代码实现。
System.out.println("play a game");
}
提前过滤掉 特殊
情况,更关注 核心
业务逻辑
小函数
我们平常开发的时候,应该编写 小而美
函数,避免 函数过长
。一般函数最好在15行以内( 建议
) 我们看看下面代码:
-------------------- before --------------------
if (age > 0 && age < 18){
System.out.println("小孩子");
}
if (number.length() == 11){
System.out.println("符合手机号");
}
-------------------- after (建议) --------------------
private static boolean isChild(int age) {
return age > 0 && age < 18;
}
private static boolean isPhoneNumber(String number) {
return number.length() == 11;
}
if (isChild(age)){
System.out.println("小孩子");
}
if (isPhoneNumber(number)){
System.out.println("符合手机号");
}
复制代码
把判断语句抽取成一个个 小函数
, 这样代码更加清晰明了。
迪米特法则
概念:
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽 可能少
的 了解
。例如 当一条语句中 一个对象出现两个 .
( student.getName().equals("张三")
) 就是代码坏味道的表现,如下代码所示。
代码:
-------------------- before --------------------
public class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
Student student = new Student("张三");
// 注意看这里,
// 这里获取 student的name属性,在根据name属性进行判断
if (StringUtils.isNotBlank(student.getName()) && student.getName().equals("张三")) {
System.out.println("我的好朋友是 " + student.getName());
}
}
-------------------- after (建议) --------------------
public class Student {
... 省略name代码
// 新增一个 判断是否是我的好朋友方法
public boolean isGoodFriend(){
return StringUtils.isNotBlank(this.name) && this.name.equals("张三");
}
}
public static void main(String[] args) {
Student student = new Student("张三");
// 根据迪米特法则,把判断逻辑,抽取到 Student 内部,暴露出方法(isGoodFriend)
if (student.isGoodFriend()){
System.out.println("我的好朋友是 " + student.getName());
}
}
IDEA/Android Studio 抽取方法快捷键: option + command + M
Map 提取对象
我们在平常开发中,会使用到 map
,但是在面向对象开发理念中,一个 map
的使用,往往就会错过了 Java Bean
。建议使用 Java Bean
更直观。如下代码:
public static void main(String[] args) {
-------------------- before --------------------
Map<String, String> studentMap = new HashMap<>();
studentMap.put("张三", "男");
studentMap.put("小红", "女");
studentMap.put("李四", "男");
studentMap.forEach((name, sex) -> {
System.out.println(name + " : " + sex);
});
-------------------- after (建议) --------------------
List<Student> students = new ArrayList<>();
students.add(new Student("张三", "男"));
students.add(new Student("小红", "女"));
students.add(new Student("李四", "男"));
for (Student student : students) {
System.out.println(student.getName() + ":" + student.getSex());
}
}
笔者在编写这点时候,有所顾虑。肯定有小伙伴跳出来说, map
和 bean
不是一样吗?用 map
我还可以省去思考如何命名 Class
呢。但是从代码规范来说,这样代码设计不是更符合 Java 面向对象
的思想吗?
Stream
Java 8 API添加了一个新的抽象称为流 Stream
,可以让你以一种声明的方式处理数据。使得代码调用起来更加优雅~ 直接来看代码:
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("张三", "男"));
students.add(new Student("李四", "男"));
students.add(new Student("小红", "女"));
students.add(new Student("小花", "女"));
students.add(new Student("小红", "女"));
-------------------- before --------------------
//统计男生个数
//传统的 for each 循环遍历
long boyCount = 0;
for (Student student : students) {
if (student.isBoy()) {
boyCount++;
}
}
System.out.println("男生个数 = " + boyCount);
-------------------- after (建议) --------------------
//统计男生个数
//stream 流遍历
long count = students.stream()
.filter(Student::isBoy) // 等同于.filter(student -> student.isBoy())
.count();
System.out.println("男生个数 = " + boyCount);
}
相比与 传统的 For
循环,更推荐大家使用 stream
遍历。 stream
流的链式调用,还有许多骚操作,如 sorted
, map
, collect
等操作符,可以省去不必要 if-else
, count
等判断逻辑。
多态
Java 三大特性之一, 多态
,相信大家都不会陌生,多态的好处就是根据对象不同类型采取不同的的行为。我们常常在编写 switch
语句的时候,如果改用多态,可以把每个分支,抽取到一个子类内的覆写函数中,这就更加灵活。
我们有这样一个需求,编写一个简单计算器方法,我们先来看一小段代码:
-------------------- before --------------------
public static int getResult(int numberA, int numberB, String operate) {
int result = 0;
switch (operate) {
case "+":
result = numberA + numberB;
break;
case "-":
result = numberA - numberB;
break;
case "*":
result = numberA * numberB;
break;
case "/":
result = numberA / numberB;
break;
}
return result;
}
-------------------- after (建议) --------------------
abstract class Operate {
abstract int compute(int numberA, int numberB);
}
class AddOperate extends Operate {
@Override
int compute(int numberA, int numberB) {
// TODO 在这里处理相关逻辑
return numberA + numberB;
}
}
... SubOperate, MulOperate, DivOperate 也和 AddOperate一样这里就不一一贴出
public static int getResult(int numberA, int numberB, String operate) {
int result = 0;
switch (operate) {
case "+":
result = new AddOperate().compute(numberA, numberB);
break;
case "-":
result = new SubOperate().compute(numberA, numberB);
break;
case "*":
result = new MulOperate().compute(numberA, numberB);
break;
case "/":
result = new DivOperate().compute(numberA, numberB);
break;
}
return result;
}
有小伙伴可能会说,你这不是更复杂了吗?
对比起单纯的 switch
,我们可以这样理解:
-
虽然在类上有所增加,但是通过多态,把对应操作的逻辑分离出来,使得代码耦合度降低。
-
加法 AddOperate getResult
-
代码可读性更好,语义更加明确。
但是这里会存在一些问题,如果我们新增一个 平方根
, 平方
等计算方式, 就需要修改 switch
里面的逻辑,新增一个条件分支。下面我们再来看看更进一步的优化。
反射
通过上面例子,我们可以进一步优化,通过 反射
生成对应的 Class
,然后在调用 compute
方法。如下代码:
public static <T extends Operate> int getResult(int numberA, int numberB, Class<T> clz) {
int result = 0;
try {
return clz.newInstance().compute(numberA, numberB);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return result;
}
}
public static void main(String[] args) {
// 调用的时候直接传递 class 即可
System.out.println(getResult(1, 2, SumOpearte.class));
}
根据传入 class
参数,然后生成对应 Opearte
处理类, 对比多态方式,我们这里采用反射,使得代码耦合度大大降低,如果在增加 平方根
, 平方
等计算方式。我们只需要 新增一个 class
继承 Opearte
即可, getResult
不用做任何修改。
需要注意的是,不是所有 switch
语句都需要这样替换, 在面对简单的 switch
语句,就不必要了, 避免 过度设计
的嫌疑。如下代码:
public String getResult(int typeCode) {
String type = "";
switch (typeCode) {
case 0:
type = "加法";
break;
case 1:
type = "减法";
break;
case 2:
type = "乘法";
break;
case 3:
type = "除法";
break;
}
return type;
}
END
如果读完觉得有收获的话,欢迎点【好看】,关注【匠心零度】,查阅更多精彩历史!!!
让我“ 好看 ”
Recommend
-
19
2019年12月11日阅读 5782[解锁新姿势] 回想起被 `if-else` 支配的恐惧,我们要打倒 if - else [解锁新姿势] 兄dei,你代...
-
21
解锁Java打包新姿势 在平时我们打包会将其打成Jar,那么在其他平台运行的时候就需要安装jre来支持运行。 那么实际上Java是可以打包成native平台所属类型的,例如: installer image ...
-
24
1. 引入 在0.5.1版本之前,用户若想删除某条记录,可以使用Spark DataSource,并将 DataSourceWriteOptions.PAYLOAD_CLASS_OPT_KEY 设置为 EmptyHoodieReco...
-
24
IDEA2020版本正式发布已经有3个月了,当时由于各方面原因(太懒)也没有去尝试新功能。于是上个周末特意去在另一个电脑上下载了最新版的IDEA,并尝试了一下。总的来说呢,体验上明显的提升。作为一个大版本的升级,自然也增加了许多新功能。个人体验了两天,支持Ja...
-
10
NFT赛道黑马耀世来袭SilkSwap解锁财富密码新姿势 SilkSwap 2021/05/15 04:49 7.6万 前言:2021年,NFT使得加密领域与传统世界不断碰撞出新的火花。更重要的是,随着某些主...
-
5
又是一年ChinaJoy。这场横跨ACGN界的盛典,不仅在“阿宅”们的朋友圈狠刷了一波屏,也成为游戏、潮玩、动漫、科技、娱乐、IP等业内品牌battle的擂台。各路营销大神是八仙过海各显神通,目的只有一个:将品牌形象打进这些Z世代的心坎儿里。其中,优酷凭借品...
-
5
微软再发「折叠屏」手机!解锁游戏“新姿势”,还有一款史上最强Surface-极果 微软再发「折叠屏」手机!解锁游戏“新姿势”,...
-
6
如何get最新护肤干货?打开丁香医生的微博、微信、客户端,你就知道了。在当下追求朋克养生的年轻人心里,丁香医生这个健康科普领域的“段子手”,已然成为大家获取最新健康知识的权威平台。最近,丁香医生更凭借《听皮肤的话》这个有梗有料的健康...
-
6
「你收到一条新的消息」 你有数过回复一条微信需要点击屏幕多少次吗? 划动底部切换...
-
5
网易视频-带你解锁Apple Watch新姿势! 网友...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK