设计模式漫谈:从依赖注入看模块解藕

本文深入探讨了面向对象设计模式中的依赖倒置原则,通过购物场景举例说明如何降低模块间的耦合度。介绍了将具体依赖转向抽象,以及通过依赖注入实现控制反转,以提高代码的灵活性和可维护性。同时,提到了依赖注入的几种方式,包括构造函数注入、接口注入和setter注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

依赖

初学面向对象时,经常会举的一个例子是购物,从这个过程里很容易抽象出Customer类:


class Customer{
  
  // 下订单
  public void order(Product product)();
  
  // 支付
  public void pay();
}

而Customer的buy行为需要传入一个Product类对象,代表这个行为需要考虑Product的内部定义:这种需要关系,就是依赖

依赖倒置原则

面向对象设计模式七大设计原则中有一条依赖倒置原则,在模块间依赖关系的设计上可以为我们提供一些指导:

细节应该依赖于抽象,而抽象不应该依赖于细节。 面向接口编程,而不是面向实现编程

这句话和面向对象设计模式中的很多理念一样,总结得过于精炼,对初学者不太友好,本鸟在这举例解释一下:

在上文提到的购物的场景中,支付通常要通过具体的支付方式,因此实际上还需要依赖一个支付工具类,供pay方法使用:


class Customer{
 	
  //具体的支付方式
  private Alipay wallet;
  
  public Customer(){
		wallet = new Alipay();
  }
    
  // 下订单
  public void order(Product product)();
  
  // 支付
  public void pay(){
    //调用支付工具进行支付动作
  }
}

这个时候Customer相对于支付工具类属于高层模块,当这里的支付工具类是某种具体的支付方式比如Alipay Wallet,那么这种依赖就是耦合度较高的——因为它依赖了一种具体的支付方式,如果Alipay的某些特性发生了改变,Customer类的实现就有可能不得不需要更改。

这样的设计就违背了细节依赖于抽象的原则,因此建议将支付工具的共性抽象出来,形成一个抽象类PayInstrument或者接口Payable,Customer依赖此抽象类,pay方法的具体实现从PayInstrument出发,避免具体支付方式特性的影响,这样的即便日后当前使用的某种具体的支付工具发生了更改,pay方法的实现也无需改动,满足了依赖倒置原则。


class Customer{
 	
  private PayInstrument payInstrument;//具体的支付方式
  
  public Customer(){
    // payInstrument = new WeChatPay();
		payInstrument = new Alipay();
  }
    
  // 下订单
  public void order(Product product)();
  
  // 支付
  public void pay(){
    //调用支付工具进行支付动作
  }
}

控制反转

虽然修改后的代码满足了依赖倒置原则,但仍然存在一点问题,如果需要更改支付方式,需要更改的Customer的构造函数,并不是一个足够好的抽象封装,对于这种问题,提出控制反转的设计理念,将高层模块(Customer)对底层模块(PayInstrument)的依赖控制权(实例化过程)移交出去,其中一种方式就是随构造函数传入依赖:


class Customer{
 	
  private PayInstrument payInstrument;//具体的支付方式
  
  public Customer(PayInstrument payInstrument){
		this.payInstrument = payInstrument;
  }
    
  // 下订单
  public void order(Product product)();
  
  // 支付
  public void pay(){
    //调用支付工具进行支付动作
  }
}

而使用传入具体支付工具实例化Customer的地方(某个外部类),称之为IoC容器

依赖注入

依赖注入是控制反转的一种实现方式,将依赖的底层模块实例传入到高层模块对象中,除了上文提到的构造函数注入方式,还有两种依赖注入方式:


class Customer implements DependencySetable {
 	
  private PayInstrument payInstrument;//具体的支付方式
  
  //接口方式注入
  @Override
  public void set(PayInstrument payInstrument){
		this.payInstrument = payInstrument;
  }
  
  //构造函数注入
  public Customer(PayInstrument payInstrument){
		this.payInstrument = payInstrument;
  }
  
  //setter方式注入
  public void setPayInstrument(PayInstrument payInstrument){
		this.payInstrument = payInstrument;
  }
    
  // 下订单
  public void order(Product product)();
  
  // 支付
  public void pay(){
    //调用支付工具进行支付动作
  }
}

依赖注入与控制反转的关系

引用一篇简书文章中的总结:

1. 控制反转是一种在软件工程中解耦合的思想,调用类只依赖接口,而不依赖具体的实现类,减少了耦合。控制权交给了容器,在运行的时候才由容器决定将具体的实现动态的“注入”到调用类的对象中。

2. 依赖注入是一种设计模式,可以作为控制反转的一种实现方式。依赖注入就是将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)。

3. 通过IoC框架,类A依赖类B的强耦合关系可以在运行时通过容器建立,也就是说把创建B实例的工作移交给容器,类A只管使用就可以。

未完待续

本来想放在自己搭的博客上:
无奈我的前端水平太差,现在markdown支持得一塌糊涂,希望假期抽时间搞一下。
既然起了这个标题,之后也会针对性学习一下,把模块解藕拓展的部分不上,也希望将来能就设计模式写一个学习感悟的小系列。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值