理解面向对象六大原则

单一职责原则

Single Responsibility Principle - SRP,就一个类而言,应该仅有一个引起它变化的原因。

单一职责原则的优点是,类的责任划分的很清楚,可以提高代码的可读性,减少维护的成本。

以京东为例,例如:

1
2
3
4
5
6
7
8
9
10
11
@interface jder : NSObject 

@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *workNumber;

// 写京东 App
- (void)writeJDApp;
// 送快递
- (void)deliverPackage;

@end

这样看来这个类就有三个职责,第一个是保存员工信息,第二个是写 App,第三个是送快递。这样,日后如果需要增加员工职能,则只好在这个类中增加,从而增加了维护成本和减少了代码可读性。

遵从单一职责原则,可以修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@interface JDer : NSObject 

@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *workNumber;

// 写京东 App
- (void)writeJDApp;
// 送快递
- (void)deliverPackage;

@end

#import "JDer.h"
@interface Development : NSObject

// 写京东 App
- (void)writeJDApp:(JDer *)jder;
// 升级聊天模块
- (void)updateIM:(JDer *)jder;

@end

#import "JDer.h"
@interface Express : NSObject

// 送快递
- (void)deliverPackage:(NSString *)packageName jder:(JDer *)jder;
// 分拣快递
- (void)sortPackage:(NSDate *)date jder:(JDer *)jder;

@end

由此可以看出,开发和快递抽出放在两个类中,分工明确。代码可读性高,如果都挤在员工类,那么后期修改起来就很麻烦。如果后期开发想升级 IM 模块,那直接在开发类中指定某一个员工去升级 IM 模块,或者在快递类中增加一个某员工去分拣某天的快递。

开闭原则

Open Close Principle - OCP,程序中的对象应该对应扩展是开放的,对于修改是封闭的。

以一个论坛模型模型为例,假设一开始只有 item 的名称,描述和图片,在后期的迭代中,需要加入小视频,允许用户上传音频,需要加入音频。如果在一个 model 类中实现,可能会造成如下问题, 如果后期还需要增加别的东西:

  1. 反复修改最早创建的 Model 类
  2. 有些开发者上传音频或视频,有些不上传,这就造成了冗余

例如:

1
2
3
4
5
6
7
8
9
@interface App : NSObject
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *desc;
@property (copy, nonatomic) NSString *picURL;

// --- 后期迭代新增 ---
@property (copy, nonatomic) NSString *videoURL;
@property (copy, nonatomic) NSString *musicURL;
@end

这样的做法,不遵从对扩展开放,修改关闭,而是直接修改了这个类。可以通过继承的方式将其拆分。

定义一个 App model 基类(假定所有 App 都需要展示名称、描述和图片):

1
2
3
4
5
@interface Item : NSObject
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *desc;
@property (copy, nonatomic) NSString *picURL;
@end

定义一个视频 App:

1
2
3
@interface VideoItem : Item
@property (copy, nonatomic) NSString *videoURL;
@end

定义一个音频 App:

1
2
3
@interface MusicItem : Item
@property (copy, nonatomic) NSString *musicURL;
@end

这样在以后的迭代中,如果要增加 HD 视频和音频,那么直接在对应的 Item 子类中添加即可。

里氏替换原则

Liskov Substitution Principle - LSP

依赖倒置原则

Dependency Inversion Principle - DIP,模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。即依赖抽象,而不依赖具体的实现。

对于定义,通俗的,可以理解为,针对 api 编程,而不是针对实现编程,不要从具体的类派生,而是通过继承抽象类或者实现接口去实现功能。

以一个宴会为例,一个宴会有中餐厨师和西餐厨师,并且已经订好了晚宴菜谱,有中餐和西餐,分别建立两个类:

1
2
3
4
5
6
7
8
9
@interface ChineseCook: NSObject
// 辣椒炒肉
- (void)capsicumFriedMeat;
@end

@interface WesternCook: NSObject
// 煎牛排
- (void)friedBeefSteak;
@end

接着有一个做菜类,等宾客到来,就开始做菜了:

1
2
3
4
5
6
7
8
@interface Cooking: NSObject

- (instanceType)initWithCooks:(NSArray *)cooks;
- (void)beginCooking;

@end

@implemetation Cooking

接口隔离原则

Interface Segregation Principle - ISP

迪米特法则

Law of Demeter(Least Knowledge Principle)- LoD