面试的时候,只要问到“面向对象”,十个人里九个人都能把三大特性背得滚瓜烂熟。封装、继承、多态,六个字一出,感觉已经是标准答案。但真到写代码的时候,问题就来了:为什么照着这个思路写出来的类,换个需求就要返工?为什么一个类全是一堆getter/setter,业务逻辑全堆在Service里?为什么说好的“可扩展”,加个需求就动老代码,测试全挂?
我在一线写了十来年代码,Java、C++、Python都混过,带过的团队里新人不少,很多人的问题不是不懂语法,而是没搞明白一件事:面向对象不是一套语法规则,是你对真实世界的一种建模能力。它解决的问题不是“怎么写代码”,而是“怎么把复杂业务拆成大脑能装得下的模块”。这篇内容我不打算给你复述教科书,而是把我这些年踩过的坑、总结出来的判断标准,以及从“会用继承”到“会设计”的那几个关键转变,一次性讲清楚。适合刚学完语法想进阶的同学,也适合写了两三年业务代码、感觉遇到瓶颈的同行。
1. 面向对象到底在解决什么问题
1.1 没有面向对象的代码长什么样
先回到一个最基础的问题:面向对象解决的是“代码复用”吗?很多人说是。但你想想,函数也能复用,模板也能复用,怎么不见它们成为主流编程思想?
我更喜欢用一个词来描述面向对象的本质:分工。一个复杂系统,如果所有逻辑都揉在一个入口函数里,就像一家公司只有一个领导,所有决定都要他拍板。人脑的短期记忆撑不住几千行的函数,一旦业务复杂到某个程度,靠“从上往下读”根本维护不了。面向对象做的事情,是把系统拆成一个个有明确职责的“部门”,再定义好部门之间的协作规则。
我见过最典型的反面教材,是刚工作那会儿接手的一个老项目。一个下单功能,从参数校验、库存扣减、价格计算、订单生成到短信通知,全写在一个一千多行的doOrder方法里。那时候改一个优惠规则,要在这堆代码里找半小时。你问责任人为什么这么写,他说“都在一个方法里,逻辑连贯,好调试”。等到第一个人离职,第二个人接手,第三个人接手,这个函数就成了谁都不敢碰的雷区。
这就是没有建模意识导致的复杂度失控。面向对象的第一步,不是让你用class,是让你先学会识别对象:这个系统里有哪几个角色?每个角色负责什么?角色之间传递什么数据?一旦这个划分清楚了,代码写起来自然就顺了。
1.2 把“流程”翻译成“协作”
这是我从过程式思维切换到对象思维时,最关键的转折点。
过程式思维是:我要做一件事,于是拆成步骤一、步骤二、步骤三。对象思维是:这件事牵涉到哪些角色,每个角色自己该干什么,然后由一条主线把这些角色串起来。
举个例子。还是下单场景,过程式会写成:
java复制// 过程式:一个方法搞定一切
public void doOrder(User user, Cart cart, Coupon coupon) {
checkUserVip(user);
BigDecimal price = calculatePrice(cart, coupon);
discountByActivity(price, ...);
Product product = new Product();
boolean success = product.reduceStock(cart); // 全是方法调用
if (!success) { throw new BizException("库存不足"); }
Order order = new Order();
order.setUserId(user.getId());
...
orderDao.insert(order);
sendSms(user.getPhone(), "下单成功");
sendWechat(user.getOpenId(), "下单成功");
}
这段代码刚写出来挺爽,所有逻辑都看得见。但注意几个问题:短信和微信通知的逻辑变了,要动这个主流程;优惠规则多了,这里会变成一长串if-else;再往后加一个“订单创建后通知仓储系统”,又要往这里插。
面向对象怎么处理?核心是让每个角色对自己的行为负责。
java复制public class User {
private Long id;
private String phone;
private String openId;
// 用户自己知道自己怎么被通知
public void notifyOrderResult(String message) {
if (StringUtils.isNotBlank(phone)) {
smsGateway.send(phone, message);
}
if (StringUtils.isNotBlank(openId)) {
wechatGateway.send(openId, message);
}
}
}
public class Product {
private String skuId;
private Stock stock;
// 库存是自己的状态,自己负责扣减和校验
public void reduceStock(int count) {
stock.deduct(count);
}
}
public class OrderService {
public void createOrder(User user, Cart cart, Coupon coupon)
