Java 面向对象设计五大基本原则
·
前言
面向对象编程不只是会写类、继承、接口,设计原则是代码好坏的分水岭。日常开发遵循设计原则,代码易扩展、易维护、低耦合;违背原则就会出现代码臃肿、改一处崩全项目、重复代码满天飞。 Java 主流遵循SOLID 五大原则,也是所有设计模式的底层思想,本篇结合 Java 代码通俗讲解。
一、单一职责原则(SRP)
定义
一个类只负责一个功能、只有一个引起它修改的原因。一个类职责过多,一处改动就可能牵连无关代码。
反例(违背)
java
运行
// 一个类既做用户数据库操作,又做文件导出,两个职责
class UserService{
// 数据库保存用户
public void saveUser(){}
// Excel导出用户数据
public void exportExcel(){}
}
正确写法(拆分)
java
运行
// 职责1:用户CRUD
class UserDbService{
public void saveUser(){}
}
// 职责2:文件导出
class UserExcelService{
public void exportExcel(){}
}
总结
高内聚、细分类,一事一类。
二、开闭原则(OCP|最重要核心原则)
定义
对扩展开放,对修改关闭。新增需求尽量新增代码,不修改原有成熟源码。原有代码不动,避免修改引入 BUG。
场景:支付方式拓展
反例:新增支付改原有代码
java
运行
class Pay{
public void pay(String type){
if("wx".equals(type)){}
// 新增支付宝,要修改pay方法,破坏原有代码
else if("ali".equals(type)){}
}
}
正例:用接口扩展,不改原有代码
java
运行
// 顶层支付接口
public interface Pay{
void pay();
}
// 微信支付实现
class WxPay implements Pay{
@Override public void pay(){}
}
// 新增支付宝:新建类,不动原有代码
class AliPay implements Pay{
@Override public void pay(){}
}
后续再加抖音支付、银行卡支付,只新建实现类即可。
三、里氏替换原则(LSP)
定义
父类出现的地方,都可以使用它的任意子类替换,程序运行不受影响。子类不能重写破坏父类原有逻辑。
要点
- 子类可以扩展父类功能,但不能修改父类原有方法实现;
- 多态本质就是里氏替换落地。
java
运行
class Animal{
public void eat(){
System.out.println("动物吃饭");
}
}
class Dog extends Animal{
@Override
public void eat(){
System.out.println("小狗啃骨头");
}
}
// 父类引用接收子类,替换无异常
public class Test{
public static void feed(Animal animal){
animal.eat();
}
public static void main(String[] args) {
feed(new Animal());
feed(new Dog()); // 子类完美替换父类
}
}
四、依赖倒置原则(DIP)
核心:面向抽象编程,不面向具体实现
- 高层模块不依赖低层模块,二者都依赖抽象(接口 / 抽象类);
- 抽象不依赖细节,细节依赖抽象。
反例:高层直接依赖具体实现
java
运行
// 高层业务直接依赖Dog具体类,新增Cat就要改代码
class Person{
public void feed(Dog dog){
dog.eat();
}
}
class Dog{void eat(){}}
优化:依赖顶层 Animal 接口
java
运行
interface Animal{void eat();}
class Dog implements Animal{@Override void eat(){}}
class Cat implements Animal{@Override void eat(){}}
// 只依赖抽象,随便换实现类
class Person{
public void feed(Animal animal){
animal.eat();
}
}
Spring IoC 容器底层就是依赖倒置思想。
五、接口隔离原则(ISP)
定义
客户端不依赖不需要的接口,大接口拆分成多个小而专用的接口,杜绝臃肿万能接口。
反例:一个大接口包含全部方法
java
运行
// 巨型接口,鸟实现类被迫实现不需要的游泳方法
interface Animal{
void fly();
void run();
void swim();
}
class Bird implements Animal{
@Override public void swim(){} // 鸟类不会游泳,被迫空实现
}
优化:拆分细粒度小接口
java
运行
interface Fly{void fly();}
interface Run{void run();}
interface Swim{void swim();}
// 鸟类只实现需要的接口
class Bird implements Fly,Run{
@Override public void fly(){}
@Override public void run(){}
}
六、博客总结
- 单一职责:类只干一件事;
- 开闭原则:加功能不加改旧代码;
- 里氏替换:子类随便替换父类;
- 依赖倒置:面向接口,不面向 new 具体类;
- 接口隔离:接口小巧专用,拒绝大杂烩接口。
日常编码时刻对照五项原则,能大幅度提升代码健壮性,也是框架源码与面试高频考点。
更多推荐
所有评论(0)