定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
以上内容来源于这里 。
再来说一下几个概念:接口,抽象基类。
在java中,类(Class)、接口(Interface)、实现类(Implement)分别是三个东西。
在OC中类(Class)由.h文件(Interface)和.m文件(Implement)组成。
所以OC中的Interface并不是java中的Interface。真正java中的接口对应于OC中的协议(Procotol)。
OC中的并没有提供直接创建抽象基类的方式,一般都是通过在协议中定义方法属性等,再由一个类去遵守该协议的方式得到抽象基类。或者你不实现协议,直接定义一个类,规定几个方法,在实现文件里写这几个空方法,也可以把它当做抽象基类。
何时使用抽象基类何时使用接口关键还是取决于待继承之间的联系。侧重于它们之间的个性差异还是共性联系。
当个性大于共性。差异较大的个性间具有某些相同的行为,相同行为的实现方式有较大的区别,使用接口。
当共性大于个性,共性相同的个体间必然具备相同的属性与行为,相同行为的实现方式具有一定区别,使用抽象基类。
总结如下:
当在差异较大的对象间寻求功能上的共性时,使用接口。
当在共性较多的对象间寻求功能上的差异时,使用抽象基类。
下面进入正题。
需求,有一个VIP学员,比如我,想要学习设计模式的课程,于是我就依赖于这个课程,但是学完之后,我又想学机器学习的课程,我还得依赖于机器学习的课程,如果我想学习其他的比如安全攻防的课程,还得继续依赖,但是我学完这些后,我还想学其他的,因为我的心里只有学习,学习使我快乐。不过,打游戏也很快乐。我还得改动我自己的方法,怎么样不改动我自己,就能学习很多的课程呢?
这时,我学习了依赖倒置原则,我应该学以致用,那么说来就来。
依赖倒置说的是不依赖其他的类,只依赖接口(抽象类不依赖实现类,实现类依赖抽象类),就是OC中的协议,那么我就创建一个协议,协议里有一个方法叫学习。
#ifndef StudyProtocol_h
#define StudyProtocol_h
@protocol StudyProtocol
- (NSString *)study;
@end
#endif /* StudyProtocol_h */
我通过协议来获取不同的课程
#import <Foundation/Foundation.h>
#import "StudyProtocol.h"
@interface VIPStudent : NSObject
- (void)studyCourse:(id<StudyProtocol> )course;
@end
再定义一个DesignPattern类,遵守StudyProtocol。
#import <Foundation/Foundation.h>
#import "StudyProtocol.h"
@interface DesignPattern : NSObject<StudyProtocol>
@end
#import "DesignPattern.h"
@implementation DesignPattern
- (NSString *)study
{
return @"学习设计模式知识";
}
@end
James老师的口头禅,这里有一个点(要是Hank老师,就是,解释一波):就是参数id<StudyProtocol>.
那么id<StudyProtocol>和id有什么区别呢?
普通id指针能指向任何对象,带协议的id只能指向遵从协议的类的对象。
这样就可以将遵守该协议的课程类传给我了。
调用:
VIPStudent *vip = [[VIPStudent alloc] init];
[vip studyCourse:[DesignPattern new]];
[vip studyCourse:[CoreML new]];
[vip studyCourse:[Safety new]];
打印结果:
s = 学习设计模式知识
s = 学习机器学习知识
s = 学习安全攻防班知识
一个很简单的Demo,但是用到了面向协议编程。这里多说一点,使用继承中的多态,也能实现该需求,但是,到底该需求使用什么方式去写,判断的依据还是个性大于共性还是共性大于个性。
大功告成,打完收工,奉上Demo