里氏替换原则
里氏替换原则的英文是Liskov Substitution Principle, 简写为LSP。英文描述为 Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it。翻译成中文就是:子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。
如下代码,父类DMTransporter 使用NSURLSession类来传输网络数据。子类
DMSecurityTransporter 集成父类 DMTransporter,增加了额外的功能,支持传输appId和appToken安全认证信息。
@interface DMTransporter : NSObject
- (void)sendRequest:(NSMutableURLRequest *)request;
@end
#import "DMTransporter.h"
@implementation DMTransporter
- (void)sendRequest:(NSMutableURLRequest *)request {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
[dataTask resume];
}
@end
@interface DMSecurityTransporter : DMTransporter
@property (nonatomic, strong) NSString *appId;
@property (nonatomic, strong) NSString *appToken;
@end
#import "DMSecurityTransporter.h"
@implementation DMSecurityTransporter
- (void)sendRequest:(NSMutableURLRequest *)request {
if (self.appId.length > 0 && self.appToken.length > 0) {
[request setValue:self.appId forHTTPHeaderField:@"app-id"];
[request setValue:self.appToken forHTTPHeaderField:@"app-token"];
}
[super sendRequest:request];
}
@end
子类DMSecurityTransporter 的设计完全符合里氏替换原则,可以替换父类出现的任何位置,并且原来代码的逻辑行为不变且正确性也没有被破坏。
下面对DMSecurityTransporter类中的sendRequest方法稍加改造一下。改造前,如果appId或者appToken没有设置,就不做校验;改造后,如果appId或者appToken没有设置,就直接抛出异常。
- (void)sendRequest:(NSMutableURLRequest *)request {
if (self.appId == nil ||
self.appId.length == 0 ||
self.appToken == nil ||
self.appToken.length == 0) {
@throw [NSException exceptionWithName:@"DMNoAuthorizationException" reason:@"未授权异常" userInfo:nil];
return;
}
[request setValue:self.appId forHTTPHeaderField:@"app-id"];
[request setValue:self.appToken forHTTPHeaderField:@"app-token"];
[super sendRequest:request];
}
- (void)demoFunction:(DMTransporter *)transporter {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@""]];
[transporter sendRequest:request];
}
改造后,如果传递给demoFunction方法的是父类DMTransporter 对象,那么demoFunction方法不会抛出异常,但如果传递的是子类DMSecurityTransporter对象,那demoFunction方法就有可能抛出异常。子类替换父类传递给demoFunction方法之后,整个程序的逻辑行为有了改变。改造之后的DMSecurityTransporter类的设计是不符合里氏替换原则的。
里氏替换是一种设计原则,用来指导继承关系中子类该如何设计的,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。
那些代码违背里氏替换原则
- 1、子类违背父类声明要实现的功能
- 2、子类违背父类对输入、输出、异常的约定
- 3、子类违背父类注释中所罗列的任何特殊说明