[感悟]深入思考,在写代码,才会成为一个合格的程序员

前言:
最近回头看了一下自己以前写过的代码,越来越觉得自己以前写的代码确实很LOW,还有需要可以改进的点,所以在这里总结一下。

逻辑条件顺序的调整,可以让程序更高效

假设现在有这样一个需求:会员,第一次登陆时,需要发送一条感谢短信。如果没有经过思考,代码可以直接这样写:

if(isUserVip && isFirstLogin)
{
	sendMsg();
}

这么写是没有问题的,下面我们来分析一下,这个&&条件,如果第一个为假就不会向下执行了,所以当把isUserVip放第一位的话,假设有5个请求,只要是VIP,就会通过,后面也会进行判断,这无疑是增加了判断次数,所以我们打算把顺序换一下,这回呢?

if(isFirstLogin && isUserVip ){
    sendMsg();
}

假设请求执行的次数是5次,那么isUserVip执行的次数是1次。
在这里插入图片描述
这不就更高效了嘛!!!!!

不必要的对象创建

我们来看看这个例子,判断用户会员是否处于有效期:

public boolean isUserVIPValid(){
	Date now = new Date();
	Calendar cal = Calendar.getInstace();
	cal.set(2020,Calendar.JANUARY,1,0,0,0);
	Date beginTime = cal.getTime();
	cal.set(2021,Calendar.JANUARY,1,0,0,0);
	Date endTime = cal.getTime();
	return now.compareTo(beginTime) >=0 && now.compareTo(endTime) <=0;
}

我们看这段代码,每次调用的时候,都会创建Calendar和Date对象。除了new Date,其他对象都是不变的。所以我们可以抽出全局变量,避免创建了不必要的对象,从而提高程序效率。

public class isUserVip{
	private static final Date BeginTime;
	private static final Date endTime;
	static{
	Calendar cal = Calendar.getInstance();
	cal.set(2020,Calendar.JANUARY,1,0,0,0);
	beginTime = cal.getTime();
	cal.set(2021,Calendar.JANUARY,1,0,0,0);
	endTime = cal.getTime();
}
public boolean isUserVIPValid(){
	Date now = new Date();
	return now.compareTo(beginTime) >=0 && now.compareTo(endTime) <=0; 
}
}

这样的代码更高效了么?

数据库查询,多查了没用的数据

数据库查询是比较耗时的操作,当数据量大的时候,更是低效率。所以尽量在查询的时候只查有用的数据。
比如举个例子:查询某个用户是否是会员。

List<Long> userIds = sqlMap.queryList("select userId from user where vip=1");
boolean isVip = userIds.contains(userId);

这个代码没有任何问题,但是把所有会员都查出来,再判断是否包含这个userId,这就很低效率了,直接查出来不好吗?

Long userId = sqlMap.queryObject("select userId from user where userId = 'userId' and vip = 1");
boolean isVip = userId != null;

实际上,我们除了把查询条件都传过去,避免数据库查多余的数据回来,还可以通过select 具体字段代替 select * , 从而使程序更高效。

不要让通知类代码影响到主流程

假设业务流程是这样的:需要用户登陆时,添加个短信通知它的粉丝。流程如下:
在这里插入图片描述
这个流程如果sendMsgNotify服务的系统挂了,或者调用sendMsgNotify失败了,那么用户登陆就失败了。
其实还有更好的方法实现,我们可以在开一个线程来处理这个方法。
在这里插入图片描述
因此我们添加通知类不是非主要,可降级的接口,所以主要流程需要我们好好的思考。

空指针的出现

NullPointException是经常出现的,所以我们在写代码的时候尽量避免低级的空指针异常。
假设如下业务场景,判断用户是否是会员:

boolean isVip = user.getUserFlag().equals("1");

这行代码,问题很大,user.getUserFlag()可能使null;

经过下面写法,可避免:

boolean isVip = "1".equals(user.getUserFlag());

日志的使用

关键业务的代码都应该使用日志。
假设你有一个转账业务,客户转装失败后进行了投诉,因为你没有打印日志,这就很难查找问题了。
日志打印在哪里呢?至少,在方法调用之前,入参需要打印,接口调用后,需要捕获一下异常,异常也需要打印日志。

public void transfer(TransferDto transferDto){
	log.info("invoke tranfer begin");
	try{
	res=transferService.transfer(transferDTO);
	}catch(Exception e)
	{
		log.error("transfer fail,cifno:{} account:{}",transferDTO.getCifno());
	}
	log.info("invoke tranfer end");
}

这里要注意info和error别弄混了,要不问题排查就不对了。

代码行数多是否可以划分呢

我们在看代码的时候总会看到一批几千行的代码。阅读起来真的费力。
我们可以将其进行划分。
一个过于冗长的函数或者一段需要注释才能让人理解的代码,可以考虑把它切分成一个功能明确的函数单元,并定义清晰简短的函数名,这样会让代码变得更加优雅。

可变因素可以做成配置化

现在假设产品提了个红包需求,圣诞节的时候,红包皮肤为圣诞节相关的,春节的时候,红包皮肤等。

if(duringChristmas)
{
 img = redPacketChristmasSkin;
}else if()
{}

如果到了元宵节的时候,运营小姐姐突然又有想法,红包皮肤换成灯笼相关的,这时候,是不是要去修改代码了,重新发布了?
从一开始,实现一张红包皮肤的配置表,将红包皮肤做成配置化呢?更换红包皮肤,只需修改一下表数据就好了。

import导入没有用到的类

如果看到代码存在没使用的import 类,没被使用到的局部变量等,就删掉吧。
这些没被引用的局部变量,如果没被使用到,就删掉吧,它又不是陈年的女儿红,留着会越发醇香。它还是会一起被编译的,就是说它还是耗着资源的呢。

查询大表时,是否加入了索引,你的sql走了索引嘛。

查询数据量比较大的表时,我们需要确认三点:

  • 你的表是否创建了索引
  • 你的查询sql是否命中索引
  • 你的sql是否还有优化余地
    一般情况下,数据量超过10万的表,就要考虑给表加索引了。哪些情况下,索引会失效呢?like通配符、索引列运算等会导致索引失效。

你的方法返回是空集合还是null?

如果返回null,调用方在忘记检测的时候,可能会抛出空指针异常。返回一个空集合呢,就省去该问题了。

mybatis查询的时候,如果返回一个集合,结果为空时也会返回一个空集合,而不是null。
正确的写法:

public static List<UserResult> getUserResultList(){
	return Collections.EMPTY_LIST;
}

初始化集合时尽量指定其大小

这个点阿里巴巴开发手册提到了:
在这里插入图片描述
假设你的map要存储的元素个数是15个左右,最优写法如下:

//initialCapacity = 15/0.75+1 = 21
Map map = new HashMap(21);

数据库查询,数据返回过多,考虑分批进行

假设你的订单表有10万数据要更新状态,不能一次性查询所有未更新的订单,要分批。
反例:

List<Order> list = sqlMap.queryList("select * from Order where status='0'");
for(Order order:list)
{
	order.setStatus(1);
	sqlMap.update(order);
}

正例:

Integer count = sqlMap.queryCount(select count(1) from Order where sattus = '0');
while(true)
{
	int size = sqlMap.batchUpdate(params);
	if(size<500)
	{
	break;
	}
}

你的接口是否考虑到幂等性, 并发情况呢?

幂等性是什么?一次和多次请求某一个资源对于资源本身应该具有同样的结果。就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。
为什么需要幂等性?

  • 用户在APP上连续点击了多次提交订单,总不能生成多个订单吧
  • 用户因为网络卡了,连续点击发送消息,接受者总不能收到重复的同一条消息吧。

假设如下业务场景:
用户点击下载按钮,系统开始下载文件,用户再次点击下载,会提示文件正在下载中。

Integer count = sqlMap.selectCount("select count(1) from excel where state=1");
if(count<0)
{
	Excel.setStatus(1);
	updateExcelStatus();
	downLoadExcel();
}else{
 "文件正在下载"
}

在这里插入图片描述
执行流程:

  • 第一步,A查询没有下载中的文件
  • 第二步,B查询没有下载中的文件
  • 第三步,A开始下载文件
  • 第四步,B开始下载文件
    这样两个文件就同时下载了,所以这个是不可行的。
    正确的方式如下:
if(updateExcelStatus(1))
{
	downLoadExcel();
}else{
	"文件正在下载中"
}

私有构造器强化工具类

工具类的方法都是静态方法,通过类来直接调用即可。但是有些调用方可能会先实例化,再用对象去调用,而这就不好了。怎么避免这种情况,让你的工具类到达可控状态呢,添加私有构造器。

基本不变的用户数据,缓存起来,性能是否有所提升呢

假设你的接口需要查询很多次数据库,获取到各中数据,然后再根据这些数据进行各种排序等等操作,这一系列猛如虎的操作下来,接口性能肯定不好。典型应用场景比如:直播列表这些。

那么,怎么优化呢?剖析你排序的各部分数据,实时变的数据,继续查DB,不变的数据,如用户年龄这些,搞个定时任务,把它们从DB拉取到缓存,直接走缓存。

因此,这个点的思考就是,在恰当地时机,适当的使用缓存。

总结

我们在开发中,好好的思考一番,一定会越来越有收获,希望对你们有用,加油,在座的各位。。。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值