【本文为个人意见,不喜就喷吧!】
最近,同事问到我,『那时候为什么从PHP转成Java?』,我想了很久,且撇开主观上的原因,当初业务重构使用java确实有很多可以说道的地方。
槽点1:哪有最好的语言,只有最合适的语言
2017年的3月份,我所维护的业务,跑的是php,使用的是thinkphp3.2 框架。刚接手的时候,这个业务还是单机,还记得有同事笑道:『这台机厉害哈,单机一年能跑出200多万的流水来!』我当然是一笑置之。可是随着业务组扩展外部渠道,开始发展这个业务,我慢慢发现这样子的系统很难搞:难维护,难扩展,难自定义,不稳定。这样子的系统,每次改到支付功能,上线都是心虚的,就怕突然出现问题,影响用户体验。于是在一段煎熬的思索和过渡后,老大开始带着我进行重构。最开始的一套方案,是准备放弃原有的tp 框架,转而使用 yii2,但由于人手不够,老大亲自操刀,架起了一套 play1.4.3(业务端) + spring-mvc(后台管理) 的一套系统,所以,我也转到了java。
我这里想说的是,一个项目选择啥语言,需要考虑团队的配置,不能上来就搞一套不切实际的东西,从实际出发,找到适合自己的语言。曾几何时,我也觉得php是世界上最好的语言,还没有之一,现在只觉得哪有最好的语言,只有最合适的语言。
......
槽点2:面向对象编程,而不是面向数组编程
也许你一定很想知道,如何才能完成从php 到 java 的转身呢?
说实话,一开始的时候,确实很痛苦,基础薄弱就不说了,精神上还有压力,因为全世界都知道【你是写java的 php程序员】。举个特别糗的例子,刚刚写spring-mvc 的时候,不知道如何接收10多个参数,于是我很活该的使用了map, 我当时想啊,这不就是我们php 的 $_GET 和 $_POST 么【想当然害死人呀!】。可是写着写着我就发现不妥了,首先,别人并不知道你的map里面有啥子参数;然后,从map取出来的值不一定有值,如果是null,还要做逻辑判断,这样子代码又麻烦了。所以呀,虽然这个代码顺利上线了,也没发生过大的故障,但是我一直把这份代码当成我的【眼中钉,肉中刺】。后面我是知道了,其实用一个对象来接收这些参数,既优雅,有便于维护,可读性也好。
下面,我把自己最恨的一段代码贴出来,告诫小伙伴们,【别这样子干啊】
1 2 3 4 5 | // 控制器 <br> @RequestMapping(value = "/list", method = RequestMethod.GET) @ResponseBody public AjaxResponse getOrderList( @RequestParam Map<String, String> searchMap) { return orderService.getListsData(searchMap); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | // 恶心的逻辑 /** * 解析搜索关键词, 组装matches * * @param matches * @param searchKey * @param searchVal * @return */ public Matches getMatchesBySearchMapEntry(Matches matches, String searchKey, String searchVal) { switch (searchKey) { case Properties.tradeType: if (Integer.parseInt(searchVal) > 0 ) { matches.eq(Columns.tradeType, searchVal); } break ; case Properties.state: int state = Integer.parseInt(searchVal); if (state == 4 ) { matches.match( "complete_status" , 1 ); // 表示已完成 } else if (state == 3 ) { matches.match( "order_status" , 0 ); // 0 表示已下单 } else if (state == 1 ) { matches.match( "order_status" , 1 ); // 1 表示已支付 } else if (state == 2 ) { matches.match( "order_status" , 2 ); // 2 表示已退款 } break ; case Properties.channelId: List<Long> belongIds = accountService.getChannelIdBelongThisUser(HttpSessionUtils.getUseAccountId()); if (belongIds == null ) { if (GeneralUtil.isObjNotZero(searchVal)) { matches.match( "channel_id" , searchVal); } } else if (belongIds.size() > 0 ) { Long id = Long.parseLong(searchVal); if (belongIds.contains(id)) { matches.match( "channel_id" , id); } else { matches.match( "channel_id" , belongIds); } } break ; case Properties.cepingId: if (GeneralUtil.isObjNotZero(searchVal)) { List<Long> cepingIds = scalePoolDao.findIdsByParentId(Long.parseLong(searchVal)); matches.match( "ceping_id" , cepingIds); } break ; case Properties.orderNo: matches.match( "order_no" , searchVal); break ; case Properties.openId: String userKey = userDao.getUserKeyByOpenidAndUserType(searchVal, 1 ); matches.match( "user_key" , userKey); break ; case Properties.userKey: matches.match( "user_key" , searchVal); break ; case Properties.createStartTime: // 下单时间 Date createStartTime = DateTimeHelper.timeFormatStringToDate(searchVal); matches.gte( "create_time" , createStartTime); break ; case Properties.createEndTime: Date createEndTime = DateTimeHelper.timeFormatStringToDate(searchVal); matches.lte( "create_time" , createEndTime); break ; case Properties.payStartTime: // 支付时间 Date payStartTime = DateTimeHelper.timeFormatStringToDate(searchVal); matches.gte( "pay_time" , payStartTime); break ; case Properties.payEndTime: Date payEndTime = DateTimeHelper.timeFormatStringToDate(searchVal); matches.lte( "pay_time" , payEndTime); break ; case Properties.userType: int userType = Integer.parseInt(searchVal); if (userType > 0 ) { matches.eq(Columns.userType, searchVal); } break ; case Properties.buyType: int buyType = Integer.valueOf(searchVal); if (buyType > 0 ) { if (buyType == BuyType.DEFAULT_COPY_ORDER.getCode()) { buyType = 0 ; } matches.eq(Columns.buyType, buyType); } break ; } return matches; } |
我现在用又臭又长来形容,这样子的code 既不方便阅读,出问题了也不好排查。而且这个代码,我还是放在个循环中来拼接的,可读性之烂可想而知。唉,要是我如果有时间了,就把这段代码改了。
其实呢,说了这么多,我就想说,包括我在内的部分 phper额,太过分依赖数组了。是啊,在php里面,没有啥是一个数组搞不定的,如果有,那就俩个。我走心看了下其它业务线的php代码,哎哟喂,好家伙。推送服务配置用数组,微信公众号配置用数组,接受请求用数组,传参用数组(个人最不喜欢的方式),好像数组少些,项目就不能正常运行了。
我并不是说不该用数组,但是数组确实不好维护。当我们思绪乱的时候,你根本记不清这个数组中有哪些参数,你也可能想不起这个return的对象有哪些对象,特别是return json的时候,如果一个数组不存在,这时候返回了null,这样子返回的接口数据,对客户端的同学来说就是一种灾难。
槽点3:一包不扫,何以扫天下
除此之外,也是我最想吐槽的一点,就是php的命名空间和包管理。虽然php现在也有了composer,但是部分包载入时,还是需要做一下手动处理的,如果是一些小白的话,那久很尴尬了。相对于php呢,java就很成熟了,maven 一出,问题基本就解决了。而php的命名空间呢,我最不爽的一点是,明明一个包没关联上,项目居然还若无其事的走着,之前没觉得咋样,写java后就觉得,这样子特别不严谨,对于我这样的强迫症者来说,不能接受!
还有就是,需要手动载入 扩展,关于这点,php做的不够好
槽点4:定时任务
此外呢,php有一点做的不够好的,定时任务!(且不说用swoole做定时任务哈,毕竟swoole还是需要点学习成本的呢!)
之前我很少用php做定时任务,如果要做的话,就要依赖linux系统crontab,或者用swoole 了。
但是在java里面,完全不是这样子的体验,在play 或者 spring-mvc ,完全就是一个job 就搞定了,哪里要这般麻烦!
槽点 5:必不可少的单元测试
然后呢,php 在单元测试方面比java 差挺多的。这里我不否认有idea 的功能,但是想要用phpunit 测试一个方法,我感觉千难万难,但是在java里,很容易就实现了!除此之外,包括我在内的部分phper,很少写单元测试,甚至有些压根就不知道这是咋回事!我觉得这是个可怕的现象,这样子的代码,连自己这关都没过,如何敢上线,如何能稳呢!基本上走过单元测试的代码,基本不存在啥语法错误,这就是妥妥的保障啊!
槽点6:你debug都不用,就不要假装在搬砖了
接下来, 包括我在内的部分phper 呢,很少去debug,有些甚至从来没这么搞过。在那段维护 thinkphp的时间了,我开始使用phpstrom的debug,说实话,代码质量,开发效率都有很大的提升!我实在不能再接受自己用 echo, var_dump 这样子去调试代码,这样子会让人觉得,这是个门外汉呢!
槽点7:php是世界上最好的语言
请放下这个想法,你会发现,无论是python,golang,java 都不比咱php差,你不知,只因你未接触!
槽点8:php代码打包功能弱
在写java前呢,每次遇到要复用的php代码呢,我都是直接copy,但是写java 一段时间后,我就觉得java 这方面比较好了,我可以通过maven 对共用的包进行打包,放到另一个项目中去,这样子既方便管理,又利于版本的迭代,省时省力!
好了,就到这里吧,接下来还会有一篇吐槽java的。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略