AI智能
改变未来

【iOS-设计模式】六大设计原则之依赖倒置原则(DIP,Dependence Inversion Principle)…


定义

高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖细节;细节应该依赖抽象。

定义解读

依赖倒置原则在程序编码中经常运用,其核心思想就是面向接口编程,高层模块不应该依赖低层模块(原子操作的模块),两者都应该依赖于抽象。我们平时常说的“针对接口编程,不要针对实现编程”就是依赖倒转原则的最好体现:接口(也可以是抽象类)就是一种抽象,只要不修改接口声明,大家可以放心大胆调用,至于接口的内部实现则无需关心,可以随便重构。这里,接口就是抽象,而接口的实现就是细节。

如果不管高层模块还是底层模块,它们都依赖于抽象,具体一点就是接口或者抽象类,只要接口是稳定的,那么任何一个的更改都不用担心其他受到影响,这就使得无论高层模块还是低层模块都可以很容易地被复用。

依赖倒转原则其实可以说是面向对象设计的标志,用哪种语言来编写程序不重要,如果编写时考虑的都是如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之那就是过程化的设计(说这句话可能不怎么好理解,再加上一句话就好理解了:面向对象的设计,出发点就是应对变化的问题)。

再举一个生活中的例子,电脑中内存或者显卡插槽,其实是一种接口,而这就是抽象;只要符合这个接口的要求,无论是用金士顿的内存,还是其它的内存,无论是4G的,还是8G的,都可以很方便、轻松的插到电脑上使用。而这些内存条就是具体实现,就是细节。

错误做法:抽象 A 依赖于实现细节 b

image.png

正确做法:抽象A依赖于抽象B,实现细节b实现抽象B

image.png

优点

代码结构清晰,维护容易。

问题提出

类 A 直接依赖类 B,假如需要将类 A 改为依赖类 C,则必须通过修改类 A 的代码来达成。这种场景下,类 A 一般是高层模块,负责复杂的业务逻辑;类 B 和类 C 是低层模块,负责基本的原子操作;假如修改类 A,会给程序带来不必要的风险。

示例

继续发工资的场景:
类 SalaryManage (类似上面说的类 A )负责工资的管理;Director (类似上面说的类 B )是总监类,现在我们要通过 SalaryManage 类来给总监发放工资了,主要代码片段如下所示:
Director.m

- (void)calculateSalary{NSLog(@\"%@总监的工资是:10000\",_strName);}

SalaryManage.m

- (void)calculateSalary:(Director *)director{[director calculateSalary];}
调用代码
Director *director = [[Directoralloc] init];director.strName  = @\"张三\";SalaryManage *salaryManage = [[SalaryManagealloc] init];[salaryManage calculateSalary:director];
输出结果
张三总监的工资是10000

这样给总监发放工资的功能已经很好的实现了,现在假设需要给经理发工资,我们发现工资管理类 SalaryManage 没法直接完成这个功能,需要我们添加新的方法,才能完成。再假设我们还需要给普通员工、财务总监、研发总监等更多的岗位发放工资,那么我们就只能不断的去修改 SalaryManage 类来满足业务的需求。产生这种现象的原因就是 SalaryManage 与 Director 之间的耦合性太高了,必须降低它们之间的耦合度才行。因此我们引入一个委托 EmployeeDelegate,它提供一个发放工资的方法定义,如下所示:

@protocol EmployeeDelegate <NSObject>- (void)calculateSalary;@end

然后我们让具体的员工类 Director、Manager 等都实现该委托方法,如下所示:

修改后的 SalaryManage 计算工资方法:

- (void)calculateSalary:(id<EmployeeDelegate>)employee{[employee calculateSalary];}
调用代码
Director *director = [[Directoralloc] init];director.strName  = @\"张三\";Manager *manager = [[Manageralloc] init];manager.strName  = @\"李四\";SalaryManage *salaryManage = [[SalaryManage alloc] init];[salaryManage calculateSalary:director];[salaryManage calculateSalary:manager];

这样修改后,无论以后怎样扩展其他的岗位,都不需要再修改 SalaryManage 类了。代表高层模块的 SalaryManage 类将负责完成主要的业务逻辑(发工资),如果需要对 SalaryManage 类进行修改,引入错误的风险极大。所以遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险。

同样,采用依赖倒置原则给多人并行开发带来了极大的便利,比如在上面的例子中,刚开始 SalaryManage 类与 Director 类直接耦合时,SalaryManage 类必须等 Director 类编码完成后才可以进行编码和测试,因为 SalaryManage 类依赖于 Director类。按照依赖倒置原则修改后,则可以同时开工,互不影响,因为 SalaryManage 与 Director 类一点关系也没有,只依赖于协议(Java和C#中称为接口)EmployeeDelegate。参与协作开发的人越多、项目越庞大,采用依赖导致原则的意义就越重大。

  • 点赞
  • 收藏
  • 分享
  • 文章举报

GS-NICE发布了177 篇原创文章 · 获赞 0 · 访问量 2726私信关注

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » 【iOS-设计模式】六大设计原则之依赖倒置原则(DIP,Dependence Inversion Principle)…