Java程序员们,你们真的懂面向对象吗?


2025年03月10日 12:56 溪流

凌晨三点钟,我盯着屏幕里第217个XXXService类按下保存键。同事突然在钉钉群里@我:"这个UserService单例被20个线程同时调用了,你赶紧查查有没有线程安全问题"。这一刻,我终于确信:Java世界集体患上了严重的对象妄想症。

一、皇帝的新衣:不存在的对象语义

打开任意一个Spring项目,映入眼帘的必然是XXXService的狂欢派对。UserServiceOrderServicePaymentService...这些类名看似遵循面向对象规范,实则是个精心设计的骗局。以用户注册为例:

1userService.register("张三", "13800138000");

这里的userService究竟代表什么对象?是注册流程的抽象?还是用户数据的具象?不,它只是个挂着羊头卖狗肉的方法集合。当我们调用orderService.createOrder()时,订单对象尚不存在;执行paymentService.pay()时,支付行为还未发生——这些Service本质上只是动词的拙劣包装。

更讽刺的是,这些类往往继承自Object基类。试问:哪个Service对象需要用到hashCode()?哪个``?当我们在Service里注入其他Service时,本质上就是在用对象语法写函数调用链,就像给自行车装方向盘还要夸它操控精准。

二、自相矛盾的牢笼:单例的黑色幽默

既然号称面向对象,为何所有Service都被@Singleton锁死?想象这样的场景:你去银行办业务,柜员说"我们全行共用一台点钞机,请排队使用"——这正是Service单例的荒诞写照。

真正的对象应有明确生命周期:订单对象随交易创建,购物车对象随会话消亡。而我们的UserService呢?从JVM启动到宕机,它像一具僵尸般永生。更可笑的是,我们还要煞有介事地讨论"这个Service是否线程安全",却不愿承认:需要讨论线程安全的对象,本就不该是对象!

对比JDK标准库的清醒设计:SimpleDateFormat不是线程安全的,所以每次都要new。而我们的Service既没有对象状态,又要假装自己是对象,最终只能靠synchronized锁或ThreadLocal来维持这个谎言。这像极了给空气装防盗门的行为艺术。

三、皇帝的新衣2.0:静态方法的替身文学

当Python开发者随手写下utils.send_email()时, 当Go程序员自然地调用pkg.ProcessPayment()时, 当C++工程师流畅地使用ProcessOrderRoutine()时,

Java开发者却在煞费苦心地:

  1. 定义接口
  2. 实现类加@Service
  3. 通过@Autowired注入
  4. 调用service.method()

就为了执行一个无状态的流程!这个四步走行为,本质上是用DI容器模拟全局函数。更荒诞的是,我们还要为此发明各种设计模式:策略模式、门面模式、代理模式...就像给自行车装喷气引擎,然后自豪地宣称这是"交通工具的模块化创新"。

那些为Service单例辩护的论调更显滑稽:"方便AOP切面"——那为什么不直接切静态方法?"便于依赖注入"——全局函数就不能依赖其他全局函数?这就像坚持用集装箱运输一瓶矿泉水,还要嘲笑别人直接用手拿不够优雅。

四、照妖镜:其他语言如何打脸

在Linux内核源码中,文件操作从不会抽象成FileService单例,而是通过file_operations结构体中的函数指针实现。C语言的智慧在于:该是对象就用结构体封装状态,该是流程就用函数直接调用。

Go语言直接打脸Java教条:

1func ProcessOrder(ctx context.Context, order Order) error {
2    validate(order)
3    createPayment(order)
4    notifyUser(order.UserID)
5}

三个包级函数直截了当,没有假装成对象的中间商赚差价。当Java开发者还在为"应该把验证逻辑放在Service还是Manager里"争论不休时,Go程序员早已写完代码去喝咖啡了。

Python的@staticmethod更是灵魂拷问:既然你们Java的Service都不需要实例变量,为何不学我们大大方方声明静态方法?非要先new一个对象再调用方法,这和脱裤子放气有什么区别

五、救赎之路:承认吧,我们不需要假对象

是时候拆穿这个皇帝的新衣了:

  1. 纯流程操作改用静态方法类(但别取名叫XXXUtils!)
  2. 需要多态实现的定义接口+默认实现类
  3. 有状态的流程才真正使用对象
  4. 依赖传递改用显式参数而非隐式注入

就像C#勇敢引入静态类,Kotlin创造object关键字,Java社区却还在用单例模式模拟静态方法。这种刻奇式的面向对象表演,既违背了封装的本意,又制造了不必要的复杂度。当我们嘲笑PHP开发者把函数和类混用时,Javaer正在用更隐蔽的方式犯着同样的错误。

下次当你准备新建XXXService类时,不妨自问:这个类需要实例化多次吗?需要继承吗?需要多态吗?如果答案都是否定,那么请记住——不是所有代码都要套对象的外壳,就像不是所有液体都要装在易拉罐里。

如需讨论,欢迎到知乎文章页面发表评论。





©2004-2025 溪流网站 保留所有权利