当前位置: 首页 > news >正文

软件工程就是一场“抽象”游戏:从 abstract 关键字到架构设计的认知跃迁

写在前面

“我接触过的抽象,好像就是抽象类和抽象方法。抽象类里只能写抽象方法,子类必须重写。其他地方好像用到的不多……对了,有时候把数据展示给前端,好像也需要‘抽象’一层,返回给前端不同的对象。”

这是很多 Java 初学者的真实困惑。我们把“抽象”理解成了一个语法关键字,而不是一种设计思想。所以当听到“软件工程就是一个抽象的过程”这种话时,总觉得玄乎,和自己写的 CRUD 没什么关系。

但事实上,你每天都在不知不觉地使用抽象——只是你没有意识到。

  • 当你定义List接口而不是直接用ArrayList时,你在抽象

  • 当你把数据库的User实体转换成UserVO返回给前端时,你在抽象

  • 当你编写一个通用的RedisService封装了缓存逻辑时,你在抽象

  • 当你按照三层架构(Controller/Service/DAO)组织代码时,你更是在做宏观的抽象

今天,我们就从“abstract 关键字”出发,一路走到架构设计层面,彻底搞懂:抽象到底是什么?为什么说它是软件工程的核心?Java 开发者如何用好抽象这把利器?

一、什么是抽象?从地图说起

想象你要去一个陌生的城市。你会怎么做?

  • 你会拿到一张城市地图。地图上画了主要道路、地标建筑、公交线路。

  • 但地图上不会画出每一棵树、每一个井盖、每一户人家的窗户。

地图就是现实世界的“抽象”:它保留了关键信息(路怎么走、地标在哪),去掉了无关细节(树木、窗户)。这让你能快速理解城市结构,而不被海量细节淹没。

软件工程中的抽象,本质上和地图一样:忽略与当前目标无关的细节,聚焦于本质特征

  • 当你调用list.get(0)时,你不关心 ArrayList 内部是数组还是链表——List接口就是抽象

  • 当你发送 HTTP 请求时,你不关心底层 TCP 三次握手——HttpClient就是抽象

  • 当你使用@Transactional时,你不关心事务的开启、提交、回滚细节——Spring 帮你抽象了

抽象的目的不是让代码“变少”,而是让复杂度“可控”。通过分层抽象,我们才能在有限的大脑容量下构建出百万行级别的软件系统。

二、Java 中的抽象语法:abstract 关键字只是冰山一角

2.1 抽象类 vs 接口:纠正常见误区

很多初学者认为:“抽象类里只能有抽象方法,抽象方法必须重写。”

这是错误的。抽象类可以有:

  • 抽象方法(没有方法体,子类必须实现)

  • 具体方法(有方法体,子类可继承或重写)

  • 成员变量、构造器(虽然不能直接实例化,但可以被子类构造器调用)

public abstract class AbstractAnimal { private String name; // 可以有字段 public AbstractAnimal(String name) { // 可以有构造器 this.name = name; } public void eat() { // 具体方法,子类可直接使用 System.out.println(name + " is eating"); } public abstract void makeSound(); // 抽象方法,子类必须实现 }

抽象类和接口的选择原则

  • 抽象类:用于表示“is-a”关系(猫是一种动物),且需要共享状态或通用行为时

  • 接口:用于表示“can-do”关系(鸟会飞、车能跑),且需要多实现或完全解耦时

Java 8 之后接口可以有默认方法和静态方法,接口的能力大大增强。现代 Java 开发中,接口的使用频率远超抽象类。

2.2 抽象方法的重写:不是“被迫”,而是“填空”

抽象方法强制子类提供具体实现,这其实是模板方法模式的体现。父类定义“骨架”,子类填充“细节”。

public abstract class DataProcessor { // 模板方法:定义了处理流程的骨架 public final void process() { loadData(); processData(); saveResult(); } protected abstract void loadData(); // 子类实现 protected abstract void processData(); // 子类实现 protected abstract void saveResult(); // 子类实现 }

这种设计让核心流程不变,而具体步骤可以灵活替换。你在 Spring 中见过的JdbcTemplateRedisTemplate都大量使用了这种思想。

三、抽象在开发中的真实应用(你每天都在用)

3.1 面向接口编程:最经典的抽象实践

// 不抽象:直接依赖具体实现 private ArrayList<User> users = new ArrayList<>(); // 抽象:依赖接口 private List<User> users = new ArrayList<>();

为什么第二段更好?因为将来你可以把ArrayList换成LinkedListCopyOnWriteArrayList,而调用方代码(users.get(0)users.add(user))完全不用改。

依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖抽象。你写的 Service 层应该依赖UserDao接口,而不是具体的UserDaoImpl

3.2 数据交互中的抽象:PO/DO/DTO/VO 的划分

你提到的“把数据展示给前端需要抽象一层”,这正是数据抽象最典型的例子。

  • PO(Persistent Object):数据库表结构的一对一映射,可能包含密码、创建时间等内部字段

  • VO(View Object):为前端定制的对象,只包含前端需要的字段,可能聚合多个 PO 的数据

如果直接把UserPO(包含passwordsalt)返回给前端,不仅泄露敏感信息,而且前端可能只需要nameavatar,传输了大量无用数据。

// 不好的抽象:直接暴露内部实体 @GetMapping("/user/{id}") public UserPO getUser(@PathVariable Long id) { ... } // 好的抽象:返回 VO @GetMapping("/user/{id}") public UserVO getUser(@PathVariable Long id) { UserPO po = userService.getById(id); return new UserVO(po.getId(), po.getName(), po.getAvatar()); }

这一层抽象,隔离了内部模型和外部契约,让前后端可以独立演进。

3.3 设计模式中的抽象:23 种模式几乎都在讲抽象

  • 策略模式:将算法抽象成接口,运行时替换

  • 工厂模式:将对象创建过程抽象,客户端不关心具体类

  • 适配器模式:将不兼容的接口抽象成统一的调用方式

  • 代理模式:在不修改原始类的情况下,抽象出额外的控制逻辑(如 Spring AOP)

3.4 分层架构:宏观层面的抽象

三层架构(Controller/Service/DAO)就是一种抽象:

  • Controller 层抽象了 HTTP 请求的处理方式

  • Service 层抽象了业务逻辑的核心流程

  • DAO 层抽象了数据存储的细节

每一层只关心它需要知道的,不关心下层具体怎么实现。这就是关注点分离

四、抽象思想的更高层次:从代码到架构

4.1 微服务中的抽象边界

微服务拆分本质上是在做领域抽象:哪些功能应该放在一起(高内聚),哪些应该隔离开(低耦合)。一个好的微服务边界,就是一个合理的业务抽象。

比如“订单服务”抽象了订单创建、支付、查询的逻辑,而“库存服务”抽象了库存扣减、锁定的逻辑。两者通过 API 契约(也是抽象)交互,互不依赖内部实现。

4.2 领域驱动设计(DDD)中的抽象

DDD 中的聚合根、值对象、领域事件都是抽象工具。它们帮助开发者在复杂的业务逻辑中,提炼出核心的领域模型,忽略非本质的技术细节。

4.3 配置与策略的抽象

现代框架大量使用声明式配置application.yml、注解)来抽象底层实现。你写一行@Cacheable,Spring 就帮你完成了缓存逻辑的编织——你不需要知道它是用 Caffeine 还是 Redis。

五、抽象不是万能的:过度抽象的代价

抽象虽好,但过度抽象也会带来问题:

  • 过度设计:为了“可能将来会变”而做多层抽象,结果代码臃肿、难以追踪

  • 性能损耗:多层抽象可能带来额外的间接调用和方法分派开销

  • 学习成本:过度抽象的代码,新人需要花大量时间理解“这层是干什么的”

抽象的原则:只抽象那些确实会变化的部分,遵循YAGNI(You Aren't Gonna Need It)原则。Rails 之父 DHH 有一句名言:“过度抽象比重复代码更糟糕。”

六、总结:抽象能力是区分“码农”和“工程师”的关键

回到最初的问题:抽象到底是什么?

  • 在语法层面,它是abstract关键字、接口、抽象类

  • 在代码层面,它是面向接口编程、设计模式、分层架构

  • 在思想层面,它是忽略细节、聚焦本质的思维方式

一个初级开发者只会写“能跑的代码”;一个高级工程师会写“能应对变化的代码”。而抽象,就是应对变化最有力的武器。

当你下次写代码时,不妨多问自己一句:
“我这里的逻辑,哪些是核心本质,哪些是当前细节?我有没有办法把本质抽象出来,让细节可以随时替换?”

培养这种思维习惯,你就已经走在从“码农”到“工程师”的路上了。

你最近写的代码中,有没有哪一块逻辑是“写死了具体实现”,但实际上可能在未来需要替换的?如果现在让你重构,你会如何引入一层抽象?欢迎在评论区写下你的案例和设计思路。

http://www.jsqmd.com/news/685259/

相关文章:

  • 交通基建市政水利钢筋网片合规供应商推荐:四川钢笆片厂家/四川钢筋网片公司/四川钢筋网片厂家/建筑用钢筋网片/成都钢笆片厂家/选择指南 - 优质品牌商家
  • mysql如何防止索引被错误使用_mysql查询计划强制约束
  • 手把手教你用MogFace人脸检测模型-large:从部署到检测全流程指南
  • dotnet-install v0.2.0 发布了
  • 图像识别技术优化
  • # 040、AutoSAR车载量产落地:工程经验、挑战与最佳实践总结
  • Zotero文献管理自动化插件:5分钟打造你的智能工作流
  • HsMod深度解析:55项创新功能全面解锁炉石传说高级玩法
  • 半导体设备盛会哪家好?聚焦产业资源对接,优选高规格平台 - 品牌2026
  • Qualcomm QCX216 LTE Cat1 bis物联网调制解调器技术解析
  • 别再手动算了!用这个在线工具5分钟搞定透明度与十六进制颜色转换
  • 别再乱删了!深入理解Adobe正版服务(AGSService)运行机制与安全移除指南
  • 如何用 bubbles 参数让自定义事件支持在 DOM 树中冒泡
  • AI 实现:智能化浇注系统设计及智能铸造仿真运行、再基于结果迭代设计
  • 2026年靠谱的筑基钢筋网片/钢筋网片高口碑品牌推荐 - 行业平台推荐
  • 8大网盘直链解析工具:告别下载限速的高效解决方案
  • SpringBoot+Vue高性能集群共享平台源码+论文
  • 2026波形梁护栏板可靠供应商TOP推荐:公路护栏板、双波护栏板、双波波形护栏、波形梁钢护栏板、省道波形护栏、路侧护栏板选择指南 - 优质品牌商家
  • 半导体芯片行业展会哪家好?主流半导体芯片行业展会助力芯片企业提升 - 品牌2026
  • StreamCap:开源自动化直播录制解决方案的技术实现与实践
  • C语言学习笔记 - 6.C概述 - C的重要性
  • 别再只会用正则了!JMeter边界提取器(Boundary Extractor)实战:5分钟搞定商品列表名称抓取
  • 2026年蜀绣定制厂家实力推荐指南:蜀绣蜀锦礼品公司/蜀绣蜀锦礼品定制/蜀绣蜀锦纪念品批发/哪里有卖蜀绣蜀锦礼品的/选择指南 - 优质品牌商家
  • 【仅限首批200名开发者】EF Core 10向量搜索企业级扩展模板(含JWT向量权限控制、租户隔离向量库、审计日志埋点)
  • 基于单目RGB视频的3D乳房表面重建技术解析
  • 如何用CoolProp在7天内掌握免费热力学物性计算?
  • # 038、AutoSAR项目实战:从需求到模型(ARXML)设计
  • 操作系统核心知识点总结
  • 量子纠错与表面码在QCCD架构中的实现与优化
  • # 039、AutoSAR项目实战:集成、测试与HIL台架验证