凌晨三点钟,我盯着屏幕里第217个XXXService类按下保存键。同事突然在钉钉群里@我:"这个UserService单例被20个线程同时调用了,你赶紧查查有没有线程安全问题"。这一刻,我终于确信:Java世界集体患上了严重的对象妄想症。
一、皇帝的新衣:不存在的对象语义
打开任意一个Spring项目,映入眼帘的必然是XXXService
的狂欢派对。UserService
、OrderService
、PaymentService
...这些类名看似遵循面向对象规范,实则是个精心设计的骗局。以用户注册为例:
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开发者却在煞费苦心地:
- 定义接口
- 实现类加
@Service
- 通过
@Autowired
注入 - 调用
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
一个对象再调用方法,这和脱裤子放气有什么区别
五、救赎之路:承认吧,我们不需要假对象
是时候拆穿这个皇帝的新衣了:
- 纯流程操作改用静态方法类(但别取名叫XXXUtils!)
- 需要多态实现的定义接口+默认实现类
- 有状态的流程才真正使用对象
- 依赖传递改用显式参数而非隐式注入
就像C#勇敢引入静态类,Kotlin创造object
关键字,Java社区却还在用单例模式模拟静态方法。这种刻奇式的面向对象表演,既违背了封装的本意,又制造了不必要的复杂度。当我们嘲笑PHP开发者把函数和类混用时,Javaer正在用更隐蔽的方式犯着同样的错误。
下次当你准备新建XXXService
类时,不妨自问:这个类需要实例化多次吗?需要继承吗?需要多态吗?如果答案都是否定,那么请记住——不是所有代码都要套对象的外壳,就像不是所有液体都要装在易拉罐里。
如需讨论,欢迎到知乎文章页面发表评论。