尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

写好Java代码,先理解这五个核心原则

写好Java代码,先理解这五个核心原则 同事把一段两百行的Service类甩给你里面塞了十二个public方法既有订单计算又有日志推送还顺手做了权限校验。你盯着那段代码脑子里冒出的念头不是“这写得真烂”而是“我该从哪里开始改”。这样的瞬间在Java开发者的日常里反复上演而真正拉开代码质量差距的往往不是语法技巧而是你是否在落笔之前想清楚了一套关于“职责”与“关系”的底层逻辑。本文要聊的五个核心原则不是来自某本畅销书的教条也不是面试官用来筛选简历的八股文。它们是你写完一段代码后运行起来、测试起来、三个月后再次打开时能真实感受到的那股“舒适感”的来源。理解了它们你写出的Java代码才谈得上“会呼吸”。一个类只应该有一个被修改的理由单一职责原则是被误解得最深的一条。很多人把它理解成“一个类只做一件事”但“一件事”的粒度极其模糊。一个用户类做了用户校验、用户存储、用户展示算不算一件事在业务眼里可能算但在维护者眼里这三者改变的原因截然不同校验规则跟着安全需求变存储结构跟着数据库迁移变展示字段跟着前端交互变。如果它们挤在同一个类里任何一方的需求变更都会导致这个类被重新编译、重新测试、重新部署。判断职责是否单一更可靠的标准是“被修改的理由”。问自己这个类如果不修改是否就无法完成某个独立的业务变化如果答案是“必须改”那这个类就承担了那一条变化轴。类应当沿着变化的方向拆分而不是沿着业务名词的边界划分。你见过那种因为加了一个字段导致五个方法都要改动的类吗那就是职责在开始时就长歪了。实操上一个立刻见效的做法是方法名里出现“and”或者“以及”就考虑拆分。saveUserAndSendNotification这种命名已经在向你招供它违背了单一职责。让每个类只有一个“老板”只有一类“老板”改需求时你才动它其他时候它老老实实待在原地这就是单一职责给你的最大的自由。对扩展保持慷慨对修改保持吝啬开闭原则看起来矛盾既要允许行为增加又不准改动已有代码。但Java的接口和抽象类天生就是为这个矛盾准备的。遇到需求变化你倾向于往现有类里加if-else还是新增一个实现类前者的路径是“修改”后者的路径是“扩展”。每写一行新的if-else你都是在把未来的可扩展性提前透支掉。举个例子一个支付处理器最初的版本只支持微信支付。后来需要支持支付宝你在方法里加了个if(type.equals(alipay))能跑但很丑。再后来要支持银联、云闪付、外币卡这个if开始变得像一棵圣诞树每个分支都挂满了对参数格式和返回值的特判。这时候你才想起来如果当初定义了一个PaymentStrategy接口每种支付方式是一个实现类新增一种支付不过是多一个类而已。开闭原则的本质是把你的代码改造成一个“插槽”而不是一片“工地”。但“对扩展开放”不意味着无脑加接口。真正意义上的开是允许替换行为而“闭”是禁止改动已有验证过的逻辑。用策略模式、模板方法模式、装饰器模式这些GoF老套路都是为了这个目的。你越是在写代码时克制住“改一改就能用”的冲动越是会在未来收到“加一个就能用”的回馈。子类想要替换父类请先治好自己的隐疾里氏替换原则听起来很理论凡是用父类的地方都能用子类替换而不产生错误。但在实际Java开发里它更多是在提醒你继承不是拿来白嫖方法的而是用来建立可替代的抽象关系的。见过太多这样的代码子类继承父类覆盖了一个方法但覆盖后直接抛异常——因为父类的那个方法对它而言“不适用”。这就是典型的替换原则破坏者。比如你有一个Bird类里面有fly()方法。然后你来了个Penguin extends Birdfly()实现里抛出UnsupportedOperationException。看起来挺符合生物学的但你的业务代码里凡是处理Bird的地方都潜伏着运行时异常。父类的契约被破坏子类的所作所为就不再是扩展而是背叛。遵循里氏替换原则的实践是不要在子类中“弱化”父类的行为。如果子类不能满足父类声明的所有约定那这个继承关系本身就该被拆散——用组合、用接口而不是硬套继承。检验方法是写测试时把父类作为参数类型然后传入所有子类实例如果哪个测试挂了而测试代码没有错那就是你的子类出了问题。更隐蔽的问题是子类返回了更窄的可见性、抛出了额外的受检异常、改了父类方法的语义。这些你以为的“细节”迟早会在某个深夜变成线上告警。接口是给调用方看的不是给你自己秀肌肉的接口隔离原则说客户端不应该被迫依赖它不用的方法。可惜很多Java代码把接口当成了“收藏夹”把所有可能用到的方法都塞进去美其名曰“统一抽象”。一个UserService接口里有login()、register()、updateAvatar()、sendSms()、uploadFile()……你的实现类被迫写了五个空方法因为接口里有这五个方法。这其实是用“大接口”来掩盖“职责混乱”。更好的做法是接口越小越好小到每个接口只对应一个“角色”的需求。调用方需要登录就只依赖Loginable需要注册就只依赖Registerable。一个类可以实现多个小接口但永远不要被迫实现它根本不需要的方法。这就是接口隔离原则——它和单一职责是同一个宇宙的两面镜子类端看职责接口端看隔离。在这个原则里最常被误用的还有“上帝接口”的变种——DTO数据传输对象和VO视图对象使用一个全字段大对象。前端要三个字段你却把整个数据库表映射的对象传过去。表面上没有接口定义问题但实际上你让调用方“依赖”了它根本不需要的字段每一次数据库结构调整都会波及前端接口。隔离的边界不只是类与方法更是数据的形态。定义细粒度的接口和DTO你的代码结构才会拥有“细胞膜”而不是一锅粥。依赖靠抽象不靠具体靠约定不靠打听依赖倒置原则是SOLID的压轴戏也是最容易被忽视的。它的核心是两点上层模块不能依赖下层模块双方都应依赖抽象抽象不能被具体实现绑定具体实现应依赖于抽象。翻译成Java代码就是你的业务逻辑层不应该直接new一个MysqlOrderRepository而是应该依赖一个OrderRepository接口。至于这个接口的实现是MySQL还是Redis还是内存是你在运行时通过依赖注入容器或工厂来决定的。你越是在一个类里直接new出它依赖的其他类你越是在拒绝未来的任何变化。为什么要依赖抽象因为抽象是稳定的、可替换的。当你把OrderRepository定义成接口的时候你就给整个系统画上了一条“约定线”业务层只跟约定对话不跟某家数据库厂商结婚。但倒置这个动词还有一层更深的含义控制流的方向和依赖声明的关系。在传统过程式代码里高层调用低层依赖自然指向低层倒置之后高层定义“我需要什么”低层去实现“我如何满足”。真正优秀的Java代码读起来像是一份“能力清单”而不是一串“执行步骤”。你要在这个模块里做什么先声明依赖接口再编写逻辑最后通过配置把这个接口绑定到某个实现上——这就是Spring容器派上用场的哲学根源。理解了这五个原则之后你会发现它们其实不是孤立的五条纪律而是在告诉你同一件事代码是写给读它的人看的而“人”包括六个月后的你自己。单一职责教你怎么切分业务变化开闭教你怎么应对需求演进里氏替换教你怎么安放继承关系接口隔离教你怎么划定调用边界依赖倒置教你怎么建立稳定连接。这五把刀一旦在你脑海里合璧你再看Java代码看到的就不再是一行行语法而是一张张由职责、边界、约定与抽象织成的网。下次你再打开一个类先别急着写public。停下来问一句这个类为什么存在它的行为会因什么样的需求而改变有没有那条接口隔离线我依赖的是名字还是本质当你开始问这些问题时你不是在写代码你是在设计一座能够持续生长的建筑。而那个建筑的地基恰好就是这五个看起来朴素、做起来需要定力的核心原则。
返回列表