前言

面向对象编程不只是会写类、继承、接口,设计原则是代码好坏的分水岭。日常开发遵循设计原则,代码易扩展、易维护、低耦合;违背原则就会出现代码臃肿、改一处崩全项目、重复代码满天飞。 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)

定义

父类出现的地方,都可以使用它的任意子类替换,程序运行不受影响。子类不能重写破坏父类原有逻辑。

要点

  1. 子类可以扩展父类功能,但不能修改父类原有方法实现;
  2. 多态本质就是里氏替换落地。

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)

核心:面向抽象编程,不面向具体实现

  1. 高层模块不依赖低层模块,二者都依赖抽象(接口 / 抽象类);
  2. 抽象不依赖细节,细节依赖抽象。

反例:高层直接依赖具体实现

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(){}
}

六、博客总结

  1. 单一职责:类只干一件事;
  2. 开闭原则:加功能不加改旧代码;
  3. 里氏替换:子类随便替换父类;
  4. 依赖倒置:面向接口,不面向 new 具体类;
  5. 接口隔离:接口小巧专用,拒绝大杂烩接口。

日常编码时刻对照五项原则,能大幅度提升代码健壮性,也是框架源码与面试高频考点。

更多推荐