您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

Java代码编写技巧实例分析

2024/2/24 5:37:28发布28次查看
开发工具不知道有多少”老”程序员还在使用 eclipse,这些程序员们要不就是因循守旧,要不就是根本就不知道其他好的开发工具的存在,eclipse 吃内存卡顿的现象以及各种偶然莫名异常的出现,都告知我们是时候寻找新的开发工具了。
更换 ide根本就不想多解释要换什么样的 ide,如果你想成为一个优秀的 java 程序员,请更换 intellij idea。使用 idea 的好处,请搜索谷歌。
别告诉我快捷键不好用
更换 ide 不在我本文的重点内容中,所以不想用太多的篇幅去写为什么更换ide。在这里,我只能告诉你,更换 ide 只为了更好、更快的写好 java 代码。原因略。
别告诉我快捷键不好用,请尝试新事物。
基于微服务的思想,构建在 b2c 电商场景下的项目实战。核心技术栈,是 spring boot + dubbo 。未来,会重构成 spring cloud alibaba 。
beanbean 使我们使用最多的模型之一,我将以大篇幅去讲解 bean,希望读者好好体会。
domain 包名根据很多 java 程序员的”经验”来看,一个数据库表则对应着一个 domain 对象,所以很多程序员在写代码时,包名则使用:com.xxx.domain ,这样写好像已经成为了行业的一种约束,数据库映射对象就应该是 domain。但是你错了,domain 是一个领域对象,往往我们再做传统 java 软件 web 开发中,这些 domain 都是贫血模型,是没有行为的,或是没有足够的领域模型的行为的,所以,以这个理论来讲,这些 domain 都应该是一个普通的 entity 对象,并非领域对象,所以请把包名改为:com.xxx.entity。
如果你还不理解我说的话,请看一下 vaughn vernon 出的一本叫做《implementing domain-driven design》(实现领域驱动设计)这本书,书中讲解了贫血模型与领域模型的区别,相信你会受益匪浅。
dto数据传输我们应该使用 dto 对象作为传输对象,这是我们所约定的,因为很长时间我一直都在做移动端 api 设计的工作,有很多人告诉我,他们认为只有给手机端传输数据的时候(input or output),这些对象成为 dto 对象。请注意!这种理解是错误的,只要是用于网络传输的对象,我们都认为他们可以当做是 dto 对象,比如电商平台中,用户进行下单,下单后的数据,订单会发到 oms 或者 erp 系统,这些对接的返回值以及入参也叫 dto 对象。
我们约定某对象如果是 dto 对象,就将名称改为 xxdto,比如订单下发oms:omsorderinputdto。
dto 转化正如我们所知,dto 为系统与外界交互的模型对象,那么肯定会有一个步骤是将 dto 对象转化为 bo 对象或者是普通的 entity 对象,让 service 层去处理。
场景比如添加会员操作,由于用于演示,我只考虑用户的一些简单数据,当后台管理员点击添加用户时,只需要传过来用户的姓名和年龄就可以了,后端接受到数据后,将添加创建时间和更新时间和默认密码三个字段,然后保存数据库。
@requestmapping("/v1/api/user")@restcontrollerpublic class userapi { @autowired private userservice userservice; @postmapping public user adduser(userinputdto userinputdto){ user user = new user(); user.setusername(userinputdto.getusername()); user.setage(userinputdto.getage()); return userservice.adduser(user); }}
我们只关注一下上述代码中的转化代码,其他内容请忽略:
user user = new user();user.setusername(userinputdto.getusername());user.setage(userinputdto.getage());
请使用工具上边的代码,从逻辑上讲,是没有问题的,只是这种写法让我很厌烦,例子中只有两个字段,如果有 20 个字段,我们要如何做呢?一个一个进行 set 数据吗?当然,如果你这么做了,肯定不会有什么问题,但是,这肯定不是一个最优的做法。
网上有很多工具,支持浅拷贝或深拷贝的 utils。举个例子,我们可以使用 org.springframework.beans.beanutils#copyproperties 对代码进行重构和优化:
@postmappingpublic user adduser(userinputdto userinputdto){ user user = new user(); beanutils.copyproperties(userinputdto,user); return userservice.adduser(user);}
beanutils.copyproperties 是一个浅拷贝方法,复制属性时,我们只需要把 dto 对象和要转化的对象两个的属性值设置为一样的名称,并且保证一样的类型就可以了。如果你在做 dto 转化的时候一直使用 set 进行属性赋值,那么请尝试这种方式简化代码,让代码更加清晰!
转化的语义上边的转化过程,读者看后肯定觉得优雅很多,但是我们再写 java 代码时,更多的需要考虑语义的操作,再看上边的代码:
user user = new user();beanutils.copyproperties(userinputdto,user);
虽然这段代码很好的简化和优化了代码,但是他的语义是有问题的,我们需要提现一个转化过程才好,所以代码改成如下:
@postmapping public user adduser(userinputdto userinputdto){ user user = convertfor(userinputdto); return userservice.adduser(user); } private user convertfor(userinputdto userinputdto){ user user = new user(); beanutils.copyproperties(userinputdto,user); return user; }
这是一个更好的语义写法,虽然他麻烦了些,但是可读性大大增加了,在写代码时,我们应该尽量把语义层次差不多的放到一个方法中,比如:
user user = convertfor(userinputdto);return userservice.adduser(user);
这两段代码都没有暴露实现,都是在讲如何在同一个方法中,做一组相同层次的语义操作,而不是暴露具体的实现。
如上所述,是一种重构方式,读者可以参考 martin fowler 的《refactoring imporving the design of existing code》(重构 改善既有代码的设计) 这本书中的 extract method 重构方式。
抽象接口定义当实际工作中,完成了几个 api 的 dto 转化时,我们会发现,这样的操作有很多很多,那么应该定义好一个接口,让所有这样的操作都有规则的进行。
如果接口被定义以后,那么 convertfor 这个方法的语义将产生变化,它将是一个实现类。
看一下抽象后的接口:
public interface dtoconvert<s,t> { t convert(s s);}
虽然这个接口很简单,但是这里告诉我们一个事情,要去使用泛型,如果你是一个优秀的 java 程序员,请为你想做的抽象接口,做好泛型吧。
我们再来看接口实现:
public class userinputdtoconvert implements dtoconvert {@overridepublic user convert(userinputdto userinputdto) {user user = new user();beanutils.copyproperties(userinputdto,user);return user;}}
我们这样重构后,我们发现现在的代码是如此的简洁,并且那么的规范:
@requestmapping("/v1/api/user")@restcontrollerpublic class userapi { @autowired private userservice userservice; @postmapping public user adduser(userinputdto userinputdto){ user user = new userinputdtoconvert().convert(userinputdto); return userservice.adduser(user); }}
review code如果你是一个优秀的 java 程序员,我相信你应该和我一样,已经数次重复 review 过自己的代码很多次了。
我们再看这个保存用户的例子,你将发现,api 中返回值是有些问题的,问题就在于不应该直接返回 user 实体,因为如果这样的话,就暴露了太多实体相关的信息,这样的返回值是不安全的,所以我们更应该返回一个 dto 对象,我们可称它为 useroutputdto:
@postmappingpublic useroutputdto adduser(userinputdto userinputdto){ user user = new userinputdtoconvert().convert(userinputdto); user saveuserresult = userservice.adduser(user); useroutputdto result = new useroutdtoconvert().converttouser(saveuserresult); return result;}
这样你的 api 才更健全。
不知道在看完这段代码之后,读者有是否发现还有其他问题的存在,作为一个优秀的 java 程序员,请看一下这段我们刚刚抽象完的代码:
user user = new userinputdtoconvert().convert(userinputdto);
你会发现,new 这样一个 dto 转化对象是没有必要的,而且每一个转化对象都是由在遇到 dto 转化的时候才会出现,那我们应该考虑一下,是否可以将这个类和 dto 进行聚合呢,看一下我的聚合结果:
public class userinputdto {private string username;private int age; public string getusername() { return username; } public void setusername(string username) { this.username = username; } public int getage() { return age; } public void setage(int age) { this.age = age; } public user converttouser(){ userinputdtoconvert userinputdtoconvert = new userinputdtoconvert(); user convert = userinputdtoconvert.convert(this); return convert; } private static class userinputdtoconvert implements dtoconvert<userinputdto,user> { @override public user convert(userinputdto userinputdto) { user user = new user(); beanutils.copyproperties(userinputdto,user); return user; } }}
然后 api 中的转化则由:
user user = new userinputdtoconvert().convert(userinputdto);user saveuserresult = userservice.adduser(user);
变成了:
user user = userinputdto.converttouser();user saveuserresult = userservice.adduser(user);
我们再 dto 对象中添加了转化的行为,我相信这样的操作可以让代码的可读性变得更强,并且是符合语义的。
再查工具类再来看 dto 内部转化的代码,它实现了我们自己定义的 dtoconvert 接口,但是这样真的就没有问题,不需要再思考了吗?
我觉得并不是,对于 convert 这种转化语义来讲,很多工具类中都有这样的定义,这中 convert 并不是业务级别上的接口定义,它只是用于普通 bean 之间转化属性值的普通意义上的接口定义,所以我们应该更多的去读其他含有 convert 转化语义的代码。
我仔细阅读了一下 guava 的源码,发现了 com.google.common.base.convert 这样的定义:
public abstract class converter<a, b> implements function<a, b> { protected abstract b doforward(a a); protected abstract a dobackward(b b); //其他略}
从源码可以了解到,guava 中的 convert 可以完成正向转化和逆向转化,继续修改我们 dto 中转化的这段代码:
private static class userinputdtoconvert implements dtoconvert<userinputdto,user> { @override public user convert(userinputdto userinputdto) { user user = new user(); beanutils.copyproperties(userinputdto,user); return user; }}
修改后:
private static class userinputdtoconvert extends converter<userinputdto, user> { @override protected user doforward(userinputdto userinputdto) { user user = new user(); beanutils.copyproperties(userinputdto,user); return user; } @override protected userinputdto dobackward(user user) { userinputdto userinputdto = new userinputdto(); beanutils.copyproperties(user,userinputdto); return userinputdto; } }
看了这部分代码以后,你可能会问,那逆向转化会有什么用呢?其实我们有很多小的业务需求中,入参和出参是一样的,那么我们变可以轻松的进行转化,我将上边所提到的 userinputdto 和 useroutputdto 都转成 userdto 展示给大家。
dto:
public class userdto { private string username; private int age; public string getusername() { return username; } public void setusername(string username) { this.username = username; } public int getage() { return age; } public void setage(int age) { this.age = age; } public user converttouser(){ userdtoconvert userdtoconvert = new userdtoconvert(); user convert = userdtoconvert.convert(this); return convert; } public userdto convertfor(user user){ userdtoconvert userdtoconvert = new userdtoconvert(); userdto convert = userdtoconvert.reverse().convert(user); return convert; } private static class userdtoconvert extends converter<userdto, user> { @override protected user doforward(userdto userdto) { user user = new user(); beanutils.copyproperties(userdto,user); return user; } @override protected userdto dobackward(user user) { userdto userdto = new userdto(); beanutils.copyproperties(user,userdto); return userdto; } }}
api:
@postmapping public userdto adduser(userdto userdto){ user user = userdto.converttouser(); user saveresultuser = userservice.adduser(user); userdto result = userdto.convertfor(saveresultuser); return result; }
当然,上述只是表明了转化方向的正向或逆向,很多业务需求的出参和入参的 dto 对象是不同的,那么你需要更明显的告诉程序:逆向是无法调用的:
private static class userdtoconvert extends converter<userdto, user> { @override protected user doforward(userdto userdto) { user user = new user(); beanutils.copyproperties(userdto,user); return user; } @override protected userdto dobackward(user user) { throw new assertionerror("不支持逆向转化方法!"); } }
看一下 dobackward 方法,直接抛出了一个断言异常,而不是业务异常,这段代码告诉代码的调用者,这个方法不是准你调用的,如果你调用,我就”断言”你调用错误了。
关于异常处理的更详细介绍,可以参考我之前的文章:如何优雅的设计 java 异常(http://lrwinx.github.io/2016/04/28/%e5%a6%82%e4%bd%95%e4%bc%98%e9%9b%85%e7%9a%84%e8%ae%be%e8%ae%a1java%e5%bc%82%e5%b8%b8/) ,应该可以帮你更好的理解异常。
bean 的验证如果你认为我上边写的那个添加用户 api 写的已经非常完美了,那只能说明你还不是一个优秀的程序员。我们应该保证任何数据的入参到方法体内都是合法的。
为什么要验证很多人会告诉我,如果这些 api 是提供给前端进行调用的,前端都会进行验证啊,你为什还要验证?
其实答案是这样的,我从不相信任何调用我 api 或者方法的人,比如前端验证失败了,或者某些人通过一些特殊的渠道(比如 charles 进行抓包),直接将数据传入到我的 api,那我仍然进行正常的业务逻辑处理,那么就有可能产生脏数据!
“对于脏数据的产生一定是致命”,这句话希望大家牢记在心,再小的脏数据也有可能让你找几个通宵!
jsr 303验证hibernate 提供的 jsr 303 实现,我觉得目前仍然是很优秀的,具体如何使用,我不想讲,因为谷歌上你可以搜索出很多答案!
再以上班的 api 实例进行说明,我们现在对 dto 数据进行检查:
public class userdto { @notnull private string username; @notnull private int age; //其他代码略}
api 验证:
@postmapping public userdto adduser(@valid userdto userdto){ user user = userdto.converttouser(); user saveresultuser = userservice.adduser(user); userdto result = userdto.convertfor(saveresultuser); return result; }
我们需要将验证结果传给前端,这种异常应该转化为一个 api 异常(带有错误码的异常)。
@postmappingpublic userdto adduser(@valid userdto userdto, bindingresult bindingresult){ checkdtoparams(bindingresult); user user = userdto.converttouser(); user saveresultuser = userservice.adduser(user); userdto result = userdto.convertfor(saveresultuser); return result;}private void checkdtoparams(bindingresult bindingresult){ if(bindingresult.haserrors()){ //throw new 带验证码的验证错误异常 }}
bindingresult 是 spring mvc 验证 dto 后的一个结果集
检查参数后,可以抛出一个“带验证码的验证错误异常”,具体异常设计可以参考如何优雅的设计 java 异常(http://lrwinx.github.io/2016/04/28/%e5%a6%82%e4%bd%95%e4%bc%98%e9%9b%85%e7%9a%84%e8%ae%be%e8%ae%a1java%e5%bc%82%e5%b8%b8/)。
拥抱 lombok上边的 dto 代码,已经让我看的很累了,我相信读者也是一样,看到那么多的 getter 和 setter 方法,太烦躁了,那时候有什么方法可以简化这些呢。
请拥抱 lombok,它会帮助我们解决一些让我们很烦躁的问题
去掉 setter 和 getter其实这个标题,我不太想说,因为网上太多,但是因为很多人告诉我,他们根本就不知道 lombok 的存在,所以为了让读者更好的学习,我愿意写这样一个例子:
@setter@getterpublic class userdto { @notnull private string username; @notnull private int age; public user converttouser(){ userdtoconvert userdtoconvert = new userdtoconvert(); user convert = userdtoconvert.convert(this); return convert; } public userdto convertfor(user user){ userdtoconvert userdtoconvert = new userdtoconvert(); userdto convert = userdtoconvert.reverse().convert(user); return convert; } private static class userdtoconvert extends converter<userdto, user> { @override protected user doforward(userdto userdto) { user user = new user(); beanutils.copyproperties(userdto,user); return user; } @override protected userdto dobackward(user user) { throw new assertionerror("不支持逆向转化方法!"); } }}
看到了吧,烦人的 getter 和 setter 方法已经去掉了。
但是上边的例子根本不足以体现 lombok 的强大。我希望写一些网上很难查到,或者很少人进行说明的 lombok 的使用以及在使用时程序语义上的说明。
比如:@data,@allargsconstructor,@noargsconstructor..这些我就不进行一一说明了,请大家自行查询资料。
bean 中的链式风格什么是链式风格?我来举个例子,看下面这个 student 的 bean:
public class student { private string name; private int age; public string getname() { return name; } public student setname(string name) { this.name = name; return this; } public int getage() { return age; } public student setage(int age) { return this; }}
仔细看一下 set 方法,这样的设置便是 chain 的 style,调用的时候,可以这样使用:
student student = new student() .setage(24) .setname("zs");
相信合理使用这样的链式代码,会更多的程序带来很好的可读性,那看一下如果使用 lombok 进行改善呢,请使用 @accessors(chain = true),看如下代码:
@accessors(chain = true)@setter@getterpublic class student { private string name; private int age;}
这样就完成了一个对于 bean 来讲很友好的链式操作。
静态构造方法静态构造方法的语义和简化程度真的高于直接去 new 一个对象。比如 new 一个 list 对象,过去的使用是这样的:
list<string> list = new arraylist<>();
看一下 guava 中的创建方式:
list<string> list = lists.newarraylist();
lists 命名是一种约定(俗话说:约定优于配置),它是指 lists 是 list 这个类的一个工具类,那么使用 list 的工具类去产生 list,这样的语义是不是要比直接 new 一个子类来的更直接一些呢,答案是肯定的,再比如如果有一个工具类叫做 maps,那你是否想到了创建 map 的方法呢:
hashmap<string, string> objectobjecthashmap = maps.newhashmap();
好了,如果你理解了我说的语义,那么,你已经向成为 java 程序员更近了一步了。
再回过头来看刚刚的 student,很多时候,我们去写 student 这个 bean 的时候,他会有一些必输字段,比如 student 中的 name 字段,一般处理的方式是将 name 字段包装成一个构造方法,只有传入 name 这样的构造方法,才能创建一个 student 对象。
接上上边的静态构造方法和必传参数的构造方法,使用 lombok 将更改成如下写法(@requiredargsconstructor 和 @nonnull):
@accessors(chain = true)@setter@getter@requiredargsconstructor(staticname = "ofname")public class student { @nonnull private string name; private int age;}
测试代码:
student student = student.ofname("zs");
这样构建出的 bean 语义是否要比直接 new 一个含参的构造方法(包含 name 的构造方法)要好很多。
当然,看过很多源码以后,我想相信将静态构造方法 ofname 换成 of 会先的更加简洁:
@accessors(chain = true)@setter@getter@requiredargsconstructor(staticname = "of")public class student { @nonnull private string name; private int age;}
测试代码:
student student = student.of("zs");
当然他仍然是支持链式调用的:
student student = student.of("zs").setage(24);
这样来写代码,真的很简洁,并且可读性很强。
使用 builder今天其实要说的是一种变种的 builder 模式,那就是构建 bean 的 builder 模式,其实主要的思想是带着大家一起看一下 lombok 给我们带来了什么。
看一下 student 这个类的原始 builder 状态:
public class student { private string name; private int age; public string getname() { return name; } public void setname(string name) { this.name = name; } public int getage() { return age; } public void setage(int age) { this.age = age; } public static builder builder(){ return new builder(); } public static class builder{ private string name; private int age; public builder name(string name){ this.name = name; return this; } public builder age(int age){ this.age = age; return this; } public student build(){ student student = new student(); student.setage(age); student.setname(name); return student; } }}
调用方式:
student student = student.builder().name("zs").age(24).build();

这样的 builder 代码,让我是在恶心难受,于是我打算用 lombok 重构这段代码:
@builderpublic class student { private string name; private int age;}
调用方式:
student student = student.builder().name("zs").age(24).build();

代理模式
正如我们所知的,在程序中调用 rest 接口是一个常见的行为动作,如果你和我一样使用过 spring 的 resttemplate,我相信你会我和一样,对他抛出的非 http 状态码异常深恶痛绝。
所以我们考虑将 resttemplate 最为底层包装器进行包装器模式的设计:
public abstract class filterresttemplate implements restoperations { protected volatile resttemplate resttemplate; protected filterresttemplate(resttemplate resttemplate){ this.resttemplate = resttemplate; } //实现restoperations所有的接口}
然后再由扩展类对 filterresttemplate 进行包装扩展:
public class extractresttemplate extends filterresttemplate { private resttemplate resttemplate; public extractresttemplate(resttemplate resttemplate) { super(resttemplate); this.resttemplate = resttemplate; } public <t> restresponsedto<t> postforentitywithnoexception(string url, object request, class<t> responsetype, object... urivariables) throws restclientexception { restresponsedto<t> restresponsedto = new restresponsedto<t>(); responseentity<t> tresponseentity; try { tresponseentity = resttemplate.postforentity(url, request, responsetype, urivariables); restresponsedto.setdata(tresponseentity.getbody()); restresponsedto.setmessage(tresponseentity.getstatuscode().name()); restresponsedto.setstatuscode(tresponseentity.getstatuscodevalue()); }catch (exception e){ restresponsedto.setstatuscode(restresponsedto.unknown_error); restresponsedto.setmessage(e.getmessage()); restresponsedto.setdata(null); } return restresponsedto; }}
包装器 extractresttemplate 很完美的更改了异常抛出的行为,让程序更具有容错性。在这里我们不考虑 extractresttemplate 完成的功能,让我们把焦点放在 filterresttemplate 上,“实现 restoperations 所有的接口”,这个操作绝对不是一时半会可以写完的,当时在重构之前我几乎写了半个小时,如下:
public abstract class filterresttemplate implements restoperations { protected volatile resttemplate resttemplate; protected filterresttemplate(resttemplate resttemplate) { this.resttemplate = resttemplate; } @override public <t> t getforobject(string url, class<t> responsetype, object... urivariables) throws restclientexception { return resttemplate.getforobject(url,responsetype,urivariables); } @override public <t> t getforobject(string url, class<t> responsetype, map<string, ?> urivariables) throws restclientexception { return resttemplate.getforobject(url,responsetype,urivariables); } @override public <t> t getforobject(uri url, class<t> responsetype) throws restclientexception { return resttemplate.getforobject(url,responsetype); } @override public <t> responseentity<t> getforentity(string url, class<t> responsetype, object... urivariables) throws restclientexception { return resttemplate.getforentity(url,responsetype,urivariables); } //其他实现代码略。。。}
我相信你看了以上代码,你会和我一样觉得恶心反胃,后来我用 lombok 提供的代理注解优化了我的代码(@delegate):
@allargsconstructorpublic abstract class filterresttemplate implements restoperations { @delegate protected volatile resttemplate resttemplate;}
这几行代码完全替代上述那些冗长的代码。
是不是很简洁,做一个拥抱 lombok 的程序员吧。
重构需求案例
项目需求
项目开发阶段,有一个关于下单发货的需求:如果今天下午 3 点前进行下单,那么发货时间是明天,如果今天下午 3 点后进行下单,那么发货时间是后天,如果被确定的时间是周日,那么在此时间上再加 1 天为发货时间。
思考与重构我相信这个需求看似很简单,无论怎么写都可以完成。
很多人可能看到这个需求,就动手开始写 calendar 或 date 进行计算,从而完成需求。
而我给的建议是,仔细考虑如何写代码,然后再去写,不是说所有的时间操作都用 calendar 或 date 去解决,一定要看场景。
对于时间的计算我们要考虑 joda-time 这种类似的成熟时间计算框架来写代码,它会让代码更加简洁和易读。
请读者先考虑这个需求如何用 java 代码完成,或先写一个你觉得完成这个代码的思路,再来看我下边的代码,这样,你的收获会更多一些:
final datetime distribution_time_split_time = new datetime().withtime(15,0,0,0);private date calculatedistributiontimebyordercreatetime(date ordercreatetime){ datetime ordercreatedatetime = new datetime(ordercreatetime); date tomorrow = ordercreatedatetime.plusdays(1).todate(); date thedayaftertomorrow = ordercreatedatetime.plusdays(2).todate(); return ordercreatedatetime.isafter(distribution_time_split_time) ? wrapdistributiontime(thedayaftertomorrow) : wrapdistributiontime(tomorrow);}private date wrapdistributiontime(date distributiontime){ datetime currentdistributiondatetime = new datetime(distributiontime); datetime plusoneday = currentdistributiondatetime.plusdays(1); boolean issunday = (datetimeconstants.sunday == currentdistributiondatetime.getdayofweek()); return issunday ? plusoneday.todate() : currentdistributiondatetime.todate() ;}
读这段代码的时候,你会发现,我将判断和有可能出现的不同结果都当做一个变量,最终做一个三目运算符的方式进行返回,这样的优雅和可读性显而易见,当然这样的代码不是一蹴而就的,我优化了 3 遍产生的以上代码。读者可根据自己的代码和我写的代码进行对比。
提高方法如果你做了 3 年+的程序员,我相信像如上这样的需求,你很轻松就能完成,但是如果你想做一个会写 java 的程序员,就好好的思考和重构代码吧。
写代码就如同写字一样,同样的字,大家都会写,但是写出来是否好看就不一定了。如果想把程序写好,就要不断的思考和重构,敢于尝试,敢于创新,不要因循守旧,一定要做一个优秀的 java 程序员。
提高代码水平最好的方法就是有条理的重构!(注意:是有条理的重构)
设计模式设计模式就是工具,而不是提现你是否是高水平程序员的一个指标。
我经常会看到某一个程序员兴奋的大喊,哪个程序哪个点我用到了设计模式,写的多么多么优秀,多么多么好。我仔细去翻阅的时候,却发现有很多是过度设计的。
业务驱动技术 or 技术驱动业务业务驱动技术 or 技术驱动业务 ?其实这是一个一直在争论的话题,但是很多人不这么认为,我觉得就是大家不愿意承认罢了。我来和大家大概分析一下作为一个 java 程序员,我们应该如何判断自己所处于的位置.
业务驱动技术: 如果你所在的项目是一个收益很小或者甚至没有收益的项目,请不要搞其他创新的东西,不要驱动业务要如何如何做,而是要熟知业务现在的痛点是什么?如何才能帮助业务盈利或者让项目更好,更顺利的进行。
技术驱动业务: 如果你所在的项目是一个很牛的项目,比如淘宝这类的项目,我可以在满足业务需求的情况下,和业务沟通,使用什么样的技术能更好的帮助业务创造收益,比如说下单的时候要进队列,可能几分钟之后订单状态才能处理完成,但是会让用户有更流畅的体验,赚取更多的访问流量,那么我相信业务愿意被技术驱动,会同意订单的延迟问题,这样便是技术驱动业务。
我相信大部分人还都处于业务驱动技术的方向吧。
所以你既然不能驱动业务,那就请拥抱业务变化吧。
代码设计一直在做 java 后端的项目,经常会有一些变动,我相信大家也都遇到过。
比如当我们写一段代码的时候,我们考虑将需求映射成代码的状态模式,突然有一天,状态模式里边又添加了很多行为变化的东西,这时候你就挠头了,你硬生生的将状态模式中添加过多行为和变化。
慢慢的你会发现这些状态模式,其实更像是一簇算法,应该使用策略模式,这时你应该已经晕头转向了。
说了这么多,我的意思是,只要你觉得合理,就请将状态模式改为策略模式吧,所有的模式并不是凭空想象出来的,都是基于重构。
java 编程中没有银弹,请拥抱业务变化,一直思考重构,你就有一个更好的代码设计!
你真的优秀吗?
真不好意思,我取了一个这么无聊的标题。
国外流行一种编程方式,叫做结对编程,我相信国内很多公司都没有这么做,我就不在讲述结对编程带来的好处了,其实就是一边 code review,一边互相提高的一个过程。既然做不到这个,那如何让自己活在自己的世界中不断提高呢?
“平时开发的时候,做出的代码总认为是正确的,而且写法是完美的。”,我相信这是大部分人的心声,还回到刚刚的问题,如何在自己的世界中不断提高呢?
答案就是:
多看成熟框架的源码
多回头看自己的代码
勤于重构
你真的优秀吗?如果你每周都完成了学习源码,回头看自己代码,然后勤于重构,我认为你就真的很优秀了。
即使也许你只是刚刚入门,但是一直坚持,你就是一个真的会写java代码的程序员了。
技能uml
不想多讨论 uml 相关的知识,但是我觉得你如果真的会写 java,请先学会表达自己,uml 就是你说话的语言,做一名优秀的 java 程序员,请至少学会这两种 uml 图:
类图
时序图
clean code
以上就是java代码编写技巧实例分析的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product