前言:
感谢您阅读这篇文章。希望大家都能有所收获。
导语
本文不是夸夸其谈的文章,不会讲很多深刻的结构。反而会说明很多基础问题和写作问题。如果读者觉得基础问题和写作问题都是问题,请忽略这篇文章,省下时间做点有意义的事情。
开发工具
不知道有多少“老”程序员还在用Eclipse。这些程序员要么墨守成规,要么就是根本不知道其他好的开发工具的存在。Eclipse的内存堵塞和各种偶然的、莫名其妙的异常都在告诉我们,是时候寻找新的开发工具了。
更换 IDE
我根本不想解释我想换什么样的IDE。如果你想成为一名优秀的Ja程序员,请改变IntelliJ的想法。关于使用IDEA的好处,请搜索Google。
别告诉我快捷键不好用
替换IDE不是我这篇文章的重点,所以我不想花太多的篇幅写我为什么要换IDE。在这里,我只能告诉你,换IDE只是为了更好更快的写出Ja代码。原因是略。
别跟我说快捷键不行,请试试新的。
bean
Bean是我们使用最多的模型之一。我会用大篇幅讲解bean,希望读者有好的体验。
域包名称
根据很多Ja程序员的“经验”,一个数据库表对应一个域对象,所以很多程序员在写代码的时候都使用包名:com.xxx.domain,这似乎已经成为业界的一个约束,数据库映射对象应该是domain。但是你错了。domain是一个域对象。通常,当我们进行传统的Ja软件Web开发时,这些领域是贫血的模型,没有行为,或者没有足够的领域模型的行为。所以按照这个理论,这些域应该是普通的实体对象,而不是域对象,所以请把包名改成:com.xxx.entity
如果你还是不明白我说的话,请看看沃恩·弗农的一本名为《实现领域驱动设计》的书,书中解释了贫血模型和领域模型的区别。相信你会受益匪浅。
DTO
我们应该使用DTO对象作为数据传输的传输对象,这是我们所同意的,因为我从事移动API的设计已经很长时间了,很多人告诉我,他们认为只有在向手机传输数据(输入或输出)时,这些对象才成为DTO对象。请注意!这种理解是错误的。只要它们被用于网络传输,我们都认为它们可以被视为DTO物体。比如在电商平台,用户下单,下单后的数据会发送到OMS或者ERP系统。这些停靠的返回值和参数也称为DTO对象。
我们同意,如果一个对象是一个DTO对象,它的名字将被改为XXDTO,如订单发出OMS:omsorderinputto。
DTO 转化
众所周知,DTO是系统与外界交互的模型对象,所以肯定会有将DTO对象转化为BO对象或普通实体对象,并让服务层处理的步骤。
场景
比如添加会员的操作,因为是用来演示的,所以我只考虑用户的一些简单数据。后台管理员点击添加用户时,只需要发送用户名和年龄即可。后端收到数据后会添加创建时间、更新时间、默认密码三个字段,然后保存数据库。
@ request mapping(& # 34;/v1/API/user & # 34;)@ RestControllerpublic class user API { @ auto wired private UserService UserService;@ post mapping public User addUser(userinputto userinputto){ User User = new User();user . set username(user input to . get username());user . setage(user input to . getage());返回userService.addUser(用户);}}我们只关注上面代码中的转换代码,其他内容请忽略:
User User = new User();user . set username(user input to . get username());user . setage(user input to . getage());请使用工具。
从逻辑上来说,上面的代码没有问题,但是这样的写法让我很累。示例中只有两个字段。如果有20个字段,我们该怎么办?你一个一个的设置数据吗?当然,如果你这样做,肯定没有问题,但绝对不是最优的做法。
互联网上有很多工具支持Utils的浅层或深层复制。例如,我们可以使用org。spring framework . beans . beans # copy properties重构和优化代码:
@ post mapping public User addUser(User input to User input to){ User User = new User();bean utils . copy properties(user input to,user);返回userService.addUser(用户);}BeanUtils.copyProperties是一种浅层复制方法。在复制属性时,我们只需要将d to对象和要转换的对象的属性值设置为相同的名称,并确保相同的类型。如果您在进行DTO转换时总是使用set来分配属性,请尝试这种方式来简化代码,使其更加清晰!
转换的语义
上面的转换过程,读者看完一定觉得优雅多了,但是我们写Ja代码的时候,需要考虑更多的语义操作,再看上面的代码:
User User = new User();bean utils . copy properties(user input to,user);虽然这段代码很好的简化和优化了代码,但是它的语义是有问题的,我们需要撤回一个转换过程,所以代码修改如下:
@PostMapping公共用户addUser(userinputto userinputto){ User User = convert for(userinputto);返回userService.addUser(用户);} private User convert for(User input to User input to){ User User = new User();bean utils . copy properties(user input to,user);返回用户;}这是比较好的语义写法。虽然有点麻烦,但是可读性大大增加。在编写代码时,我们应该尝试将相似的语义级别放入一个方法中,例如:
user user = convert for(user input to);返回userService.addUser(用户);这两段代码都没有公开实现,都是在说如何在同一个方法中做一组同级别的语义操作,而不是公开具体的实现。
如上所述,它是一种重构方法,读者可以参考马丁·福勒的《重构使现有代码的设计变得贫瘠》中的重构方法。
抽象接口定义
在实际工作中完成几个API的DTO转换时,我们会发现有很多这样的操作,所以要定义一个接口,让所有这样的操作都可以有规律的进行。
如果定义了接口,这个方法的语义就会改变,它就是一个实现类。
看抽象接口:
公共接口DTOConvert & lts,T & gt{ T次转换;}虽然这个接口很简单,但是这里告诉我们一件事:要使用泛型,如果你是一个优秀的Ja程序员,请为你想做的抽象接口做泛型。
让我们再次看看接口实现:
公共类UserInputDTOConvert实现dto convert { @ override public User convert(userinputto UserInputDTO){ User User = new User();bean utils . copy properties(user input to,user);返回用户;这样重构之后,我们发现现在的代码是如此的简洁和规范:
@ request mapping(& # 34;/v1/API/user & # 34;)@ RestControllerpublic class user API { @ auto wired private UserService UserService;@ post mapping public User addUser(User input to User input to){ User User = new User input to convert()。convert(user input to);返回userService.addUser(用户);如果你是一个优秀的Ja程序员,我相信你应该像我一样} }复习几次代码code。
我们再来看看这个拯救用户的例子。你会发现API里的返回值有些问题。问题是不应该直接返回用户实体,因为如果这样的话,会暴露太多关于实体的信息,这样的返回值是不安全的,所以我们应该返回一个DTO对象,我们可以称之为UserOutputDTO:
@ post mapping public User output to addUser(User input to User input to){ User User = new User input to convert()。convert(user input to);user se user result = userservice . adduser(用户);user output dto result = new useroutto convert()。convert touser(se user result);返回结果;}这样你的API会更健全。
不知道读者看完这段代码有没有发现其他问题。作为一名优秀的Ja程序员,请看看我们刚刚抽象出的这段代码:
User user = new UserInputDTOConvert()。convert(user input to);你会发现像new这样的DTO变换对象是不必要的,每个变换对象只有遇到DTO变换才会出现。那么就要考虑这个类是否可以和DTO聚合,看看我的聚合结果:
公共类UserInputDTO {私有字符串username私人年龄;公共字符串get username(){ return username;} public void set username(String username){ this . username = username;} public int getAge(){ return age;} public void setAge(int age){ this . age = age;} public User convert touser(){ userinputtoconvert userinputtoconvert = new userinputtoconvert();user convert = userinputtoconvert . convert(this);返回convert}私有静态类UserInputDTOConvert实现DTOConvert & ltUserInputDTO,User & gt{ @ Override public User convert(User input to User input to){ User User = new User();bean utils . copy properties(user input to,user);返回用户;}}}那么API中的转换是通过:
User user = new UserInputDTOConvert()。convert(user input to);user se user result = userservice . adduser(用户);变成了:
user user = user input to . convert touser();user se user result = userservice . adduser(用户);我们向DTO对象添加了转换行为,我相信这可以使代码更具可读性和语义性。
再次检查工具类
再来看DTO内部转换的代码,实现了自己定义的DTOConvert接口,但是这样做真的没有问题,不需要再考虑一下吗?
我不这么认为。对于Convert的转换语义,很多工具类中都有这样的定义。在这种情况下,Convert不是业务层面的接口定义,只是普通意义上的接口定义,用于普通bean之间的属性值转换,所以我们要多读一些包含转换语义的代码。
我仔细看了一下GUA的源代码,找到了com.google.common.base.Convert的定义:
公共抽象类转换器& lt一、B& gt;实现函数& lt一、B& gt;{受保护的抽象B do forward(A A);受保护摘要A doBackward(B B);//其他遗漏}从源代码中可以知道,在GUA中Convert可以完成正向转换和反向转换,在我们的DTO中继续修改这段转换后的代码:
私有静态类UserInputDTOConvert实现DTOConvert & ltUserInputDTO,User & gt{ @ Override public User convert(User input to User input to){ User User = new User();bean utils . copy properties(user input to,user);返回用户;}}修改后:
私有静态类UserInputDTOConvert扩展转换器& ltUserInputDTO,User & gt{ @ Override protected User do forward(User input to User input to){ User User = new User();bean utils . copy properties(user input to,user);返回用户;} @ Override protected User input to doBackward(User用户){ User input to User input to = new User input to();BeanUtils.copyProperties(user,user input to);返回userInputDTO看完这部分代码,你可能会问,逆变换有什么用?其实我们有很多小业务需求,输入输出参数都是一样的,可以很容易的转换。我把上面提到的UserInputDTO和UserOutputDTO转换成UserDTO给大家看。
DTO:
公共类UserDTO {私有字符串username私人年龄;公共字符串get username(){ return username;} public void set username(String username){ this . username = username;} public int getAge(){ return age;} public void setAge(int age){ this . age = age;} public User convert touser(){ UserDTOConvert UserDTOConvert = new UserDTOConvert();user convert = userdtoconvert . convert(this);返回convert} public UserDTO convert for(User User){ UserDTO convert UserDTO convert = new UserDTO convert();UserDTO convert = UserDTO convert . reverse()。转换(用户);返回convert}私有静态类UserDTOConvert扩展转换器& ltUserDTO,User & gt{ @ Override protected User do forward(User dto User dto){ User User = new User();bean utils . copy properties(user dto,user);返回用户;} @ Override protected User dto doBackward(User User){ User dto User dto = new User dto();BeanUtils.copyProperties(user,userdo);返回userDTO} } }API:
@ post mapping public User dto addUser(User dto User dto){ User User = User dto . convert touser();user se result user = userservice . adduser(用户);user dto result = user dto . convert for(se result user);返回结果;当然,以上只是指明了转换的正向或反向,很多业务需求的DTO对象是不一样的,所以你需要更明确的告诉程序,反向是不能调用的:
私有静态类UserDTOConvert扩展转换器& ltUserDTO,User & gt{ @ Override protected User do forward(User dto User dto){ User User = new User();bean utils . copy properties(user dto,user);返回用户;} @ Override protected userdo doBackward(User User){ throw new assertion error(& # 34;不支持反向转换方法!");}}看doBackward方法,直接抛出断言异常,不是业务异常。这段代码告诉调用者不允许您调用这个方法。如果你调用它,我会“断言”你调用错了。
关于异常处理更详细的介绍,请参考本文:如何优雅地设计Ja异常(lrwinx.github.io/2016/04/28/…),这应该有助于你更好地理解异常。
bean 的验证
如果你觉得我上面写的添加用户的API很完美,那只能说明你不是一个优秀的程序员。我们应该确保输入方法体的任何数据都是合法的。
为什么要验证
很多人会跟我说,如果把这些API提供给前端调用,前端会验证。为什么需要验证它们?
其实答案是这样的。我从来不相信任何调用我的API或者方法的人,比如前端验证失败,或者有人通过一些特殊的渠道直接把数据传入我的API(比如查尔斯抢包),那么我仍然进行正常的业务逻辑处理,然后可能会产生脏数据!
“对脏数据的产生一定是致命的”,希望大家牢记这句话,哪怕一个小小的脏数据也可能让你一夜之间找到几个!
jsr 303验证
我觉得目前hibernate提供的jsr 303的实现还是很优秀的。我不想说怎么用,因为你可以在谷歌上搜索出很多答案!
以工作中的API示例为例,我们现在检查DTO数据:
公共类UserDTO { @ NotNull私有字符串username@ NotNull private int age//ja学习交流:737251827进入领取学习资源和十年开发经验提问,免费解答!//其他代码省略}API验证:
@ post mapping public User dto addUser(@ Valid User dto User dto){ User User = User dto . convert touser();user se result user = userservice . adduser(用户);user dto result = user dto . convert for(se result user);返回结果;}我们需要把验证结果传递给前端,这个异常要转换成api异常(有错误码的异常)。
@ post mapping public userdo addUser(@ Valid userdo userdo,binding result binding result){ checkd toparams(binding result);user user = user dto . convert touser();user se result user = userservice . adduser(用户);user dto result = user dto . convert for(se result user);返回结果;} private void checkdtoparams(绑定结果binding result) {if(绑定结果。has errors()){//抛出新的验证错误,验证码异常}}BindingResult是spring MVC验证DTO后的一个结果集,可以参考spring官方文档(spring.io/)。
检查完参数后,可以抛出一个带有验证码的验证错误异常。
详情请参考这篇优秀的文章。
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 % A1ja % E5 % BC % 82% E5 % B8 % B8/
拥抱龙目岛
我已经厌倦了阅读上面的DTO代码,我相信读者也厌倦了看到这么多的Getter和Setter方法。那时候怎么做才能简化他们呢?
请拥抱龙目岛,它会帮助我们解决一些让我们很苦恼的问题。
去掉Setter和Getter
其实我不想说这个标题,因为网上太多了,但是因为很多人告诉我他们根本不知道龙目岛的存在,所以为了让读者更好的学习,我想写一个这样的例子:
@ Setter @ getter public class userdo { @ not null私有字符串username@ NotNull private int agepublic User convert touser(){ UserDTOConvert UserDTOConvert = new UserDTOConvert();user convert = userdtoconvert . convert(this);返回convert} public UserDTO convert for(User User){ UserDTO convert UserDTO convert = new UserDTO convert();UserDTO convert = UserDTO convert . reverse()。转换(用户);返回convert}私有静态类UserDTOConvert扩展转换器& ltUserDTO,User & gt{ @ Override protected User do forward(User dto User dto){ User User = new User();bean utils . copy properties(user dto,user);返回用户;} @ Override protected userdo doBackward(User User){ throw new assertion error(& # 34;不支持反向转换方法!");}}}看,烦人的Getter和Setter方法都去掉了。
但以上例子不足以说明龙目岛的强大。希望写一下在网上很难找到或者很少解释的lombok的用法,以及程序在使用时的语义解释。
比如:@ data,@ allargsconstructor,@ noargsconstructor…不一一解释了。请自行核对信息。
豆中的链样式
什么是链式?我给你举个例子。看下面这个学生豆:
公共类学生{私有字符串名称;私人年龄;公共字符串getName(){ return name;} public Student set name(String name){ this . name = name;还这个;} public int getAge(){ return age;} public Student setAge(int age){ return this;}}仔细看看set方法。这种设置是链式的。打电话时,可以这样用:
学生学生=新生()。setAge(24)。设置名称(& # 34;zs & # 34);相信这样的链码使用合理的话,更多的程序会带来很好的可读性。我们看看是不是用lombok来改善。请使用@Accessors(chain = true)并查看以下代码:
@ Accessors(chain = true)@ Setter @ getter public class Student { private String name;私人年龄;}这样就完成了一个对bean友好的链式操作。
静态施工方法
静态构造方法的语义和简化真的比直接去一个新对象要高。例如,一个列表对象new过去是这样的:
列表& lt字符串& gtlist = new ArrayList & lt& gt();看看番石榴里的创作方法:
列表& lt字符串& gtlist = lists . new ArrayList();列表的命名是约定俗成的(俗话说:约定胜于配置),也就是说列表是List的一个工具类,那么用List的工具类来生成列表是不是更直接?答案是肯定的。比如有一个叫地图的工具类,你有没有想到创建地图的方法?
HashMap & ltString,String & gtobjectobject hashmap = maps . new hashmap();好吧,如果你理解了我说的语义,那么你就离成为Ja程序员更近了一步。
回头看看刚才的学生,很多时候,我们写学生bean的时候,它会有一些必填字段,比如学生中的姓名字段。一般的处理方法是将name字段包装成一个构造函数,只有传入类似name的构造函数才能创建一个Student对象。
把上面的静态构造方法和所需参数的构造方法连接起来,用lombok改成下面的写法(@RequiredArgsConstructor和@NonNull):
@ Accessors(chain = true)@ Setter @ Getter @ RequiredArgsConstructor(static name = & # 34;ofName & # 34)公共课学生{ @非空私有字符串名;私人年龄;}测试代码:
student student = student . of name(& # 34;zs & # 34);这样构造的bean的语义是否比直接新建带参数的构造函数(带名称的构造函数)好很多。
当然,看了很多源代码,我想相信先把Name的静态构造方法换成Of会更简洁:
@ Accessors(chain = true)@ Setter @ Getter @ RequiredArgsConstructor(static name = & # 34;的& # 34;)公共课学生{ @非空私有字符串名;私人年龄;}测试代码:
student student = student . of(& # 34;zs & # 34);当然,他仍然支持链式调用:
student student = student . of(& # 34;zs & # 34).setAge(24);这样写代码确实简洁易读。
使用生成器
不想再解释构建器模式了。读者可以先看看Head的builder模式。
其实我今天要讲的是builder模型的一个变种,也就是构建bean的builder模型。其实主要是想看看龙目岛给我们带来了什么。
查看学生类的原始构建器状态:
公共类学生{私有字符串名称;私人年龄;公共字符串getName(){ return name;} public void set name(String name){ this . name = name;} public int getAge(){ return age;} public void setAge(int age){ this . age = age;}公共静态生成器Builder(){ return new Builder();}公共静态类生成器{私有字符串名称;私人年龄;公共生成器名称(字符串名称){ this.name = name还这个;} public Builder age(int age){ this . age = age;还这个;}公共学生构建(){学生学生=新生();student.setAge(年龄);student.setName(名称);返校生;}}}调用方法:
学生student = Student.builder()。姓名(& # 34;zs & # 34).年龄(24)。build();
这种构建器代码让我恶心,所以我要用lombok重构这段代码:
@Builderpublic class Student {私有字符串名称;私人年龄;}通话方式:
学生student = Student.builder()。姓名(& # 34;zs & # 34).年龄(24)。build();代理模式
众所周知,在程序中调用rest接口是一种常见的行为。如果你和我一样用过spring的RestTemplate,相信你会和我一样讨厌他抛出的非http状态码。
因此,我们认为RestTemplate是设计包装器模式的底层包装器:
公共抽象类FilterRestTemplate实现RestOperations { protected volatile rest template rest template;受保护的FilterRestTemplate(rest template rest template){ this . rest template = rest template;}//实现RestOperations的所有接口}然后通过扩展类扩展FilterRestTemplate:
公共类ExtractRestTemplate扩展FilterRestTemplate { private rest template rest template;public ExtractRestTemplate(rest template rest template){ super(rest template);this . rest template = rest template;} public & ltT & gt重新响应到& ltT & gtpostForEntityWithNoException(字符串url,对象请求,类& ltT & gt响应类型,对象…uriVariables)抛出RestClientException { restresponsed to & lt;T & gtrest responsed to = new rest responsed to & lt;T & gt();响应实体& ltT & gttResponseEntitytry { tResponseEntity = rest template . postforentity(URL,request,responseType,uri variables);rest responsed to . setdata(tresponseentity . getbody());rest responsed to . set message(tresponseentity . getstatuscode()。name());rest responsed to . setstatuscode(tresponseentity . getstatuscodevalue());} catch(Exception e){ rest responsed to . setstatuscode(rest responsed to。未知_错误);rest responsed to . set message(e . getmessage());restResponseDTO.setData(空);} return restResponseDTO包装器ExtractRestTemplate完美地改变了异常抛出的行为,使程序更具容错性。这里,我们不考虑ExtractRestTemplate完成的函数。让我们把重点放在FilterRestTemplate上,“实现RestOperations的所有接口”。这个操作肯定不是一时半会儿能完成的。当时我在重构前写了差不多半个小时,如下:
公共抽象类FilterRestTemplate实现RestOperations { protected volatile rest template rest template;受保护的FilterRestTemplate(rest template rest template){ this . rest template = rest template;} @覆盖public & ltT & gtT getForObject(字符串url,Class & ltT & gt响应类型,对象…uriVariables)抛出rest client exception { return rest template . getfor object(URL,responseType,uri variables);} @覆盖public & ltT & gtT getForObject(字符串url,Class & ltT & gtresponseType,Map & lt字符串,?& gturiVariables)抛出rest client exception { return rest template . getfor object(URL,responseType,uri variables);} @覆盖public & ltT & gtT getForObject(URI url,Class & ltT & gtresponseType)抛出rest client exception { return rest template . getfor object(URL,response type);} @覆盖public & ltT & gt响应实体& ltT & gtgetForEntity(字符串url,Class & ltT & gt响应类型,对象…uriVariables)抛出rest client exception { return rest template . getforentity(URL,responseType,uri variables);}//其他实现代码省略。。。}相信看完上面的代码,你会和我一样觉得恶心。后来,我用lombok (@Delegate)提供的代理注释优化了我的代码:
@AllArgsConstructorpublic抽象类FilterRestTemplate实现rest operations { @ Delegate protected volatile rest template rest template;这几行代码完全取代了上面提到的那些冗长的代码。
这不是很简单吗?做一个拥抱龙目岛的程序员。
重组
需求案例
项目要求
在项目开发阶段,有下单和发货的需求:如果今天下午3点前下单,发货时间就是明天;如果今天下午3点以后下单,发货时间是后天;如果确定的时间是周日,那么再多一天就是发货时间。
思考与重建
我相信这个要求看似简单,无论怎么写都可以完成。
很多人可能看到这个需求,开始写日历或者日期来计算,从而完成需求。
而我的建议是,仔细想好代码怎么写,然后再写,不是说所有的时间操作都用日历或者日期来解决,一定要看场景。
对于时间的计算,要考虑类似成熟的时间计算框架joda-time来编写代码,这样会使代码更加简洁,可读性更强。
请先考虑如何用Ja代码完成这个需求,或者先写一个如何完成这个代码的思路,再看下面我的代码,这样你会收获更多:
最终日期时间分布时间分割时间=新日期时间()。withTime(15,0,0,0);private Date calculateddistributiontimebyordercreatetime(Date orderCreateTime){ DateTime ordercreatedetime = new DateTime(orderCreateTime);明天的日期= ordercreatedatetime . plus days(1)。toDate();date theDayAfterTomorrow = ordercreatedatetime . plus days(2)。toDate();return ordercreatedatetime . is after(DISTRIBUTION _ TIME _ SPLIT _ TIME)?wrappdistributiontime(theDayAfterTomorrow):wrappdistributiontime(明天);}私有日期wrapDistributionTime(日期分布时间){ DateTime currentDistributionDateTime = new DateTime(分布时间);DateTime plus oneday = currentdistributiondatetime . plus days(1);boolean issun day =(datetime constants。SUNDAY = = currentdistributiondatetime . getday ofweek());返回问题日?plus oneday . todate():current distribution datetime . todate();}在看这段代码的时候,你会发现我把判断和可能的不同结果看成一个变量,最后作为一个三眼运算符返回。这种优雅和可读性是显而易见的。当然,这个代码不是一蹴而就的。我对上面生成的代码进行了三次优化。读者可以将他们自己的代码与我自己的代码进行比较。
提高方法
如果你已经做了3年程序员,相信你很容易满足以上要求,但是如果你想成为一个会写Ja的程序员,那就好好想想,重构代码。
写代码就像写作一样。每个人都可以写出一样的字,但是写出来是否好看就不一定了。想要写出好的程序,就要不断思考和重构,敢于尝试和创新,不要墨守成规,做一个优秀的Ja程序员。
提高代码水平最好的方法就是有条不紊的重构!(注:是有组织的重建)
设计模式
设计模式是工具,不是你是不是高级程序员的指标。
经常看到一个程序员兴奋地大喊,哪个程序哪个点我用了设计模式,写得多优秀多好。我仔细看了一遍,发现很多都是过度设计。
服务驱动技术或技术驱动服务。
业务驱动技术还是技术驱动业务?其实这是一个有争议的话题,但很多人并不这么认为。我觉得只是大家不愿意承认罢了。我来给大家大致分析一下,作为一个Ja程序员,我们应该如何判断自己的位置。
业务驱动技术:如果你的项目是一个收入很少或者没有收入的项目,请不要搞其他创新的东西,不要驱动业务怎么做,而是要熟悉业务现在的痛点是什么。如何帮助企业盈利,或者让项目进行得更好更顺利?
技术驱动的业务:如果你的项目是一个很棒的项目,比如淘宝,我可以在满足业务需求的情况下和商家沟通,用什么样的技术可以更好的帮助商家创造营收,比如下单的时候,订单状态处理可能需要几分钟,但是会让用户有更流畅的体验,赚取更多的访问流量,所以我相信商家愿意技术驱动,会同意订单的延迟。
我相信大部分人还是在业务驱动技术的方向。
因此,既然你不能推动业务,请拥抱业务变化。
代码设计
我一直在做Ja后端项目,经常会有一些变动,相信大家都遇到过。
例如,当我们编写一段代码时,我们考虑将需求映射到代码的状态模式中。突然有一天,状态模式出现了很多行为变化,然后你就挠头了。您突然向状态模式添加了太多的行为和变更。
慢慢的,你会发现这些状态模式其实更像是算法的集群,你应该使用策略模式。这个时候,你应该已经迷茫了。
说了这么多,我的意思是,只要你觉得合理,请把状态模式改成策略模式。所有的模式都不是空想象出来的,而是基于重构。
Ja编程没有银弹,请拥抱业务变化,不断思考重构,你会有更好的代码设计!
你真的很好吗?
很抱歉取了这么无聊的标题。
国外流行的一种编程方法叫结对编程。相信国内很多公司都没有这么做,所以我就不说结对编程带来的好处了。其实就是一个代码评审的同时互相提高的过程。//ja学习交流:737251827进入领取学习资源和十年开发经验提问,免费解答!既然做不到这一点,那我该如何活在自己的世界里,提升自己呢?
“平时开发的时候,做出来的代码总是被认为是正确的,写的很完美。”我相信这是大多数人的心声。让我们回到刚才的问题。怎样才能在自己的世界里不断进步?
答案是:
看成熟框架的源代码。
更经常地回顾你的代码。
勤于重建
你真的很好吗?如果你每周学完源代码,回头看看自己的代码,然后在重构上下功夫,我觉得你真的很优秀。
即使你可能刚刚入门,但你是一个真正会写ja代码的程序员。
技能
用户模式
关于UML我不想多讨论,但是我觉得如果你真的会写Ja,请先学会表达自己。UML是你说的语言。要成为一名优秀的Ja程序员,请至少学习这两个UML图:
类图
程序图表
干净的代码
我觉得保持代码简洁易读是代码最基本的保证。如果有一天为了程序的效率而降低了这两点,我觉得是可以理解的。另外,你没有理由随意挥霍你的代码。
读者可以阅读罗伯特·c·马丁出版的《干净的代码》一书。
可以参考美团谈tech.meituan.com/clean-code的文章..
你也可以看看阿里的Ja编码规范(yq.aliyun.com/articles/69……)。
无论如何,请保持你的代码整洁。
Linux 基础命令
其实这和写Ja没什么关系,但是Linux确实在很多情况下搭载了运行Ja的容器。请学习Linux的基本命令。
总结
Ja是一个大系统。今天的讨论不涉及框架和架构相关的知识,只讨论如何写出好的代码。
本文从小的方面写Ja程序,到大的方面,如何写好Ja程序,告诉读者如何提高自己的编码水平。
希望每一个看这篇文章的人都能成为一名优秀的Ja程序员。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。