点击上方IT牧场,选择置顶或者星标
技术干货每日送达
来源:https://juejin.im/post/6844903983744548877
前言
在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题。甚至有些复制粘贴过来,不加以整理规范。往往导致项目后期难以维护,更别说后续接手项目的人。所以啊,我们要编写出优雅的代码,方便你我他,岂不美哉?
下面分享一些我在开发中常用的编码中小建议
,如有不妥,欢迎大家一起交流学习。
卫语句
卫语句,就是把复杂的条件表达式拆分成多个条件表达式。比如 多个if-elseif-else
嵌套, 可以拆分成多个if
。如下面代码
代码:
--------------------before--------------------publicvoidtoday(){if(isWeekend()){if(isFee()){System.out.println("studyAndroid");}else{System.out.println("playagame");}}else{System.out.println("gotowork");}}--------------------after(建议)--------------------publicvoidtoday(){//提前过滤掉`特殊情况`if(!isWeekend()){System.out.println("gotowork");return;//提前return}//提前过滤掉`特殊情况`if(isFee()){System.out.println("studyAndroid");return;//提前return}//更关注于`核心业务`代码实现。System.out.println("playagame");}
提前过滤掉
特殊
情况,更关注核心
业务逻辑
小函数
我们平常开发的时候,应该编写小而美
函数,避免函数过长
。一般函数最好在15行以内(建议
) 我们看看下面代码:
--------------------before--------------------if(age>0&&age<18){System.out.println("小孩子");}if(number.length()==11){System.out.println("符合手机号");}--------------------after(建议)--------------------privatestaticbooleanisChild(intage){returnage>0&&age<18;}privatestaticbooleanisPhoneNumber(Stringnumber){returnnumber.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--------------------publicclassStudent{privateStringname;publicStudent(Stringname){this.name=name;}publicStringgetName(){returnname;}}publicstaticvoidmain(String[]args){Studentstudent=newStudent("张三");//注意看这里,//这里获取student的name属性,在根据name属性进行判断if(StringUtils.isNotBlank(student.getName())&&student.getName().equals("张三")){System.out.println("我的好朋友是"+student.getName());}}--------------------after(建议)--------------------publicclassStudent{...省略name代码//新增一个判断是否是我的好朋友方法publicbooleanisGoodFriend(){returnStringUtils.isNotBlank(this.name)&&this.name.equals("张三");}}publicstaticvoidmain(String[]args){Studentstudent=newStudent("张三");//根据迪米特法则,把判断逻辑,抽取到Student内部,暴露出方法(isGoodFriend)if(student.isGoodFriend()){System.out.println("我的好朋友是"+student.getName());}}
IDEA/Android Studio 抽取方法快捷键:
option + command + M
Map 提取对象
我们在平常开发中,会使用到map
,但是在面向对象开发理念中,一个map
的使用,往往就会错过了Java Bean
。建议使用Java Bean
更直观。如下代码:
publicstaticvoidmain(String[]args){--------------------before--------------------Map<String,String>studentMap=newHashMap<>();studentMap.put("张三","男");studentMap.put("小红","女");studentMap.put("李四","男");studentMap.forEach((name,sex)->{System.out.println(name+":"+sex);});--------------------after(建议)--------------------List<Student>students=newArrayList<>();students.add(newStudent("张三","男"));students.add(newStudent("小红","女"));students.add(newStudent("李四","男"));for(Studentstudent:students){System.out.println(student.getName()+":"+student.getSex());}}
笔者在编写这点时候,有所顾虑。肯定有小伙伴跳出来说,map
和bean
不是一样吗?用map
我还可以省去思考如何命名Class
呢。但是从代码规范来说,这样代码设计不是更符合Java 面向对象
的思想吗?
Stream
Java 8 API添加了一个新的抽象称为流Stream
,可以让你以一种声明的方式处理数据。使得代码调用起来更加优雅~ 直接来看代码:
publicstaticvoidmain(String[]args){List<Student>students=newArrayList<>();students.add(newStudent("张三","男"));students.add(newStudent("李四","男"));students.add(newStudent("小红","女"));students.add(newStudent("小花","女"));students.add(newStudent("小红","女"));--------------------before--------------------//统计男生个数//传统的foreach循环遍历longboyCount=0;for(Studentstudent:students){if(student.isBoy()){boyCount++;}}System.out.println("男生个数="+boyCount);--------------------after(建议)--------------------//统计男生个数//stream流遍历longcount=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--------------------publicstaticintgetResult(intnumberA,intnumberB,Stringoperate){intresult=0;switch(operate){case"+":result=numberA+numberB;break;case"-":result=numberA-numberB;break;case"*":result=numberA*numberB;break;case"/":result=numberA/numberB;break;}returnresult;}--------------------after(建议)--------------------abstractclassOperate{abstractintcompute(intnumberA,intnumberB);}classAddOperateextendsOperate{@Overrideintcompute(intnumberA,intnumberB){//TODO在这里处理相关逻辑returnnumberA+numberB;}}...SubOperate,MulOperate,DivOperate也和AddOperate一样这里就不一一贴出publicstaticintgetResult(intnumberA,intnumberB,Stringoperate){intresult=0;switch(operate){case"+":result=newAddOperate().compute(numberA,numberB);break;case"-":result=newSubOperate().compute(numberA,numberB);break;case"*":result=newMulOperate().compute(numberA,numberB);break;case"/":result=newDivOperate().compute(numberA,numberB);break;}returnresult;}
有小伙伴可能会说,你这不是更复杂了吗?
对比起单纯的switch
,我们可以这样理解:
虽然在类上有所增加,但是通过多态,把对应操作的逻辑分离出来,使得代码耦合度降低。
如果要修改对应加法
的逻辑, 我们只需要修改对应AddOperate
类就可以了。避免直接修改getResult
方法
代码可读性更好,语义更加明确。
但是这里会存在一些问题,如果我们新增一个平方根
,平方
等计算方式, 就需要修改switch
里面的逻辑,新增一个条件分支。下面我们再来看看更进一步的优化。
反射
通过上面例子,我们可以进一步优化,通过反射
生成对应的Class
,然后在调用compute
方法。如下代码:
publicstatic<TextendsOperate>intgetResult(intnumberA,intnumberB,Class<T>clz){intresult=0;try{returnclz.newInstance().compute(numberA,numberB);}catch(InstantiationException|IllegalAccessExceptione){e.printStackTrace();returnresult;}}publicstaticvoidmain(String[]args){//调用的时候直接传递class即可System.out.println(getResult(1,2,SumOpearte.class));}
根据传入class
参数,然后生成对应Opearte
处理类, 对比多态方式,我们这里采用反射,使得代码耦合度大大降低,如果在增加平方根
,平方
等计算方式。我们只需要 新增一个class
继承Opearte
即可,getResult
不用做任何修改。
需要注意的是,不是所有
switch
语句都需要这样替换, 在面对简单的switch
语句,就不必要了, 避免过度设计
的嫌疑。如下代码:
publicStringgetResult(inttypeCode){Stringtype="";switch(typeCode){case0:type="加法";break;case1:type="减法";break;case2:type="乘法";break;case3:type="除法";break;}returntype;}
干货分享
最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!
•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术群讨论
近期热文
•LinkedBlockingQueue vs ConcurrentLinkedQueue•解读Java 8 中为并发而生的 ConcurrentHashMap•Redis性能监控指标汇总•最全的DevOps工具集合,再也不怕选型了!•微服务架构下,解决数据库跨库查询的一些思路•聊聊大厂面试官必问的 MySQL 锁机制
关注我
喜欢就点个"在看"呗^_^