单一职责原则
Single Responsibility Principle,SRP。
定义
一个类、接口、方法只做一件事,应该有且仅有一个原因引起类的变更。
难点:单一职责原则最难划分的就是职责,职责是没有一个量化的标准。
优点
1、类的复杂度降低,实现什么职责都有清晰明确的定义
2、可读性提高
3、可维护性提高
4、变更引起的风险降低,接口修改只对相应的实现类有影响,对其他的接口无影响,对系统的扩展性、维护性有非常大的帮助
示例
这是一个用户类,但是并没有遵循单一职责原则,应该把用户信息抽取成一个BO(Business Object,业务对象),把行为抽取成一个Biz(Bussiness Logic,业务逻辑)
应用
单一职责原则很难在项目中得到体现,因为单一职责原则会引起类的剧增,而且过分细分类的职责也会人为地增加系统的复杂性。而且职责的划分每个人的认知并不一样,这并不是一个量化的标准,所以在设计的时候需要多方面的考虑。
单一职责适用于接口、类、方法等,下图的方法定义就不是一个好的定义
定义的方法职责不明确,参数多样性,后期其他人维护会很麻烦。
代码
下面的代码是一个员工类,我们新增加需求时经常会把相关的方法增加到类中,这样类是负责了多余的工作,导致做了多件事
@interface Employee : NSObject
//============ 初始需求 ============
@property (nonatomic, copy) NSString *name; //员工姓名
@property (nonatomic, copy) NSString *address; //员工住址
@property (nonatomic, copy) NSString *employeeID; //员工ID
//============ 新需求 ============
//计算薪水
- (double)calculateSalary;
@end
应该新建一个会计部门类,分离出方法
#import "Employee.h"
//会计部门类
@interface FinancialApartment : NSObject
//计算薪水
- (double)calculateSalary:(Employee *)employee;
@end
如果日后需求员工又分成多种,高级员工、中级员工、低级员工多种,有的人可能是新建3个员工类的子类,有的人可能是增加了一个属性,这要看具体的需求,看员工是否有其他的区分。当然第一种以后会有更好的扩展性,也更符合单一职责原则,相对应的话FinancialApartment类也需要有多种计算薪水的方法,而不是用type等区分。但是在一般的工作开发中,我们往往会以第二种去完成,这样当时的改动是最简便快捷的,但是以后系统扩展会更加麻烦。
里氏替换原则
Liskov Substitution Principle,LSP。
定义
如果对每一个类型为S的对象o1,都有类型为T的对 象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变 化,那么类型S是类型T的子类型。通俗点讲,只要父类能出现的地方子类就可以出现,而且 替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
原则规范
1、子类必须完全实现父类的方法。
2、子类可以有自己的个性,里氏替换原则可以正着用,但是不能反过来用。
3、覆盖或实现父类的方法时输入参数可以被放大。
4、覆写或实现父类的方法时输出结果可以被缩小。
继承优点
1、代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性。
2、提高代码的重用性。
3、子类可以形似父类,但又异于父类。
4、提高代码的可扩展性。
5、提高产品和项目的开放性。
应用
我们在写程序时,尽量不要重载或者重写父类已经实现的方法,抽象方法未实现所以要可以重写,因为重写父类已经实现的方法可能会改变父类原有的行为,这样替换就会出现问题,原则规范中的3、4也是为了可以实现子类替换父类,但是这样重写很容易出问题,因此要慎重。里氏替换原则就是避免了人们对于继承的滥用,检验是否真正符合继承关系,考虑继承体系是否还可以支持以后的需求变更。
依赖倒置原则
Dependence Inversion Principle,DIP.
原则规范
1、高层模块不应该依赖低层模块,两者都应该依赖其抽象。
2、抽象不应该依赖细节。
3、细节应该依赖抽象。
正确做法:抽象A依赖于抽象B,实现细节b实现抽象B
依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的的再组装就是高层模块。在Objective-C中,抽象指的是委托,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成。
好处
采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。
示例
坏的设计:
@interface BMW : NSObject
- (void)BMWstart;
@end
@implementation BMW
- (void)BMWstart {
NSLog(@"宝马车开起来了");
}
@end
@interface Benz : NSObject
- (void)Benzstart;
@end
@implementation Benz
- (void)Benzstart {
NSLog(@"奔驰车开起来了");
}
@end
我们设计了BMW和Benz类,各自有一个启动方法,这样设计就会造成强依赖,没有使用依赖倒置方法。
好的设计:
@protocol CarProtocol <NSObject>
- (void)start;
@end
@interface BMW : NSObject<CarProtocol>
@end
@implementation BMW
- (void)start {
NSLog(@"宝马车开起来了");
}
@interface Benz : NSObject<CarProtocol>
@end
@implementation Benz
- (void)start {
NSLog(@"奔驰车开起来了");
}
@end