开发原则四:依赖倒置DIP

依赖倒置原则是Robert C. Martin在1996年为“C++Reporter”所写的专栏Engineering Notebook的第三篇,后来加入到他在2002年出版的经典著作“Agile Software Development, Principles,Parrerns,and Practices”一书中。
依赖倒置原则(Dependency Inversion Principle,简称DIP)是面向对象设计的一个重要原则,它提倡抽象不应该依赖于具体实现,而是应该依赖于抽象。
1.3 依赖的关系种类

  1. 零耦合关系:如果两个类之间没有耦合关系,称之为零耦合
  2. 直接耦合关系: 具体耦合发生在两个具体类(可实例化的)之间,经由一个类对另一个类的直接引用造成。
  3. 抽象耦合关系: 抽象耦合关系发生在一个具体类和一个抽象类(或者java接口)之间,使两个必须发生关系的类之间存在最大的灵活性。
    依赖倒转原则就是要针对接口编程,不要针对实现编程。这就是说,应当使用接口或者抽象类进行变量的类型声明,参数的类型声明,方法的返回类型说明,以及数据类型的转换等

核心思想是高层模块不应该直接依赖于低层模块,而是通过抽象来解耦。

高层模块和低层模块都应该依赖于抽象接口或抽象类,而不是具体实现类。
抽象接口或抽象类不能依赖于具体实现类。
依赖关系的转移应该是由高层模块定义抽象接口,低层模块去实现该接口。

遵循依赖倒置原则的好处:

降低了模块之间的耦合度,使得系统更加灵活、可扩展和可维护。
提高了代码的可测试性,利于进行单元测试和模块测试。
有助于构建稳定的架构,减少对修改的影响范围。

依赖倒置实例

实例一

// 抽象接口
public interface IMessage {
    void send(String message);
}

// 具体实现类
public class EmailMessage implements IMessage {
    @Override
    public void send(String message) {
        // 发送邮件的具体实现
    }
}

public class SMMessage implements IMessage {
    @Override
    public void send(String message) {
        // 发送短信的具体实现
    }
}

// 高层模块依赖于抽象接口
public class NotificationService {
    private IMessage message;

    public NotificationService(IMessage message) {
        this.message = message;
    }

    public void notifyUser(String content) {
        message.send(content);
    }
}

在这个示例中,高层模块NotificationService依赖于抽象接口IMessage,而不直接依赖于具体实现类
EmailMessage或SMMessage。通过使用依赖倒置原则,我们可以更灵活地切换不同的消息发送方式,只需要传入不同的实现类即可。

实例二

public class Benz {
    public void run() {
        System.out.println("奔驰跑起来了!");
    }
}
public class Driver {
    private String name;
    public Driver(String name) {
        this.name = name;
    }
    public void driver(Benz benz) {
        benz.run();
    }
}
public class CarTest {
    public static void main(String[] args) {
        Benz benz = new Benz();
        Driver driver = new Driver("张三");
        driver.driver(benz);
    }
}

有一个驾驶员张三可以驾驶奔驰汽车, 于是最开始我们思考, 会有一个驾驶员类, 有一个奔驰汽车类. 随着业务的发展, 我们发现, 驾驶员张三还可以驾驶宝马.
于是,我们定义一个BM类

public class BM {
    public void run() {
        System.out.println("宝马跑起来了!");
    }
}

这时, 张三如果想要开宝马, 就要将宝马注册在他名下.

public class Driver {
    private String name;
    public Driver(String name) {
        this.name = name;
    }
    public void driver(Benz benz) {
        benz.run();
    }
    public void driver(BM bm) {
        bm.run();
    }
}
public class CarTest {
    public static void main(String[] args) {
        Benz benz = new Benz();
        BM bm = new BM();
        Driver driver = new Driver("张三");
        driver.driver(benz);
        driver.driver(bm);
    }
}

似乎这样就可以了, 但是这样有什么问题呢?
如果张三有一天要开大众, 还要增加一个大众车类, 同时还得挂载司机名下.
不是所有的人都要开奔驰, 开宝马. 开大众.
这就是面向实现编程的问题, 接下来我们就要考虑面向接口编程.

public interface ICar {
    public void run();
}
public class Benz implements ICar{
    public void run() {
        System.out.println("奔驰跑起来了!");
    }
}
public class BM implements ICar{
    public void run() {
        System.out.println("宝马跑起来了!");
    }
}
public interface IDriver {
    public void driver(ICar car);
}
public class Driver implements IDriver{
    @Override
    public void driver(ICar car) {
        car.run();
    }
}
public class CarTest {
    public static void main(String[] args) {
        IDriver driver = new Driver();
        driver.driver(new Benz());
        driver.driver(new BM());
    }
}

修改后的代码, 提炼出来一个IDriver接口和ICar接口, 面向接口编程. IDriver的实现类驾驶员可以driver任何类型的汽车, 所以传入参数也是一个接口ICar. 任何类型的汽车, 都可以通过实现ICar接口注册为一种新的汽车类型. 当客户端调用的时候, 将对应的汽车传入就可以了.

实例三

模拟信息发送

class Email {
    public String getInfo() {
        return "电子邮件信息";
    }
}
 
class Person {
    public void receive(Email email) {
        System.out.println(email.getInfo());
    }
}
 
public static void main(String[] args) {
    Person person = new Person();
    person.receive(new Email());
}

如果获取信息的对象是微信,短信等等,则需要增加类,同时Person也要增加相应的接收方法。如下所示:

class Email {
    public String getInfo() {
        return "电子邮件信息";
    }
}
class WeiXin {
    public String getInfo() {
        return "微信件信息";
    }
}
 
class Person {
    public void receiveByEmail(Email email) {
        System.out.println(email.getInfo());
    }
    public void receiveByWeiXin(WeiXin weiXin) {
        System.out.println(weiXin.getInfo());
    }
}
 
public static void main(String[] args) {
    Person person = new Person();
    person.receiveByEmail(new Email());
    person.receiveByWeiXin(new WeiXin());
}

很显然,这样做很麻烦。可以做如下修改:
引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖。因为Email, WeiXin等属于接收的范围,它们各自实现IReceiver接口便符合依赖倒置的原则。修改如下所示:

public interface IReceiver {
    public String getInfo();
}
 
class Email implements IReceiver {
 
    @Override
    public String getInfo() {
        return "电子邮件接收信息";
    }
}
 
class WeiXin implements IReceiver {
 
    @Override
    public String getInfo() {
        return "微信接收信息";
    }
}
 
class Person {
    public void receiver(IReceiver receiver) {
        System.out.println(receiver.getInfo());
    }
}
 
public static void main(String[] args) {
    Person person = new Person();
    person.receiver(new Email());
    person.receiver(new WeiXin());
}

这样做,即便增加新的方式,也只需要新增类去实现IReceiver接口即可,不必修改过多的代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值