前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅谈面向对象的六大原则

浅谈面向对象的六大原则

作者头像
s_在路上
发布2018-11-28 09:55:50
5450
发布2018-11-28 09:55:50
举报
文章被收录于专栏:iOS 开发杂谈iOS 开发杂谈

image.png

原则一、单一职责原则(Single Responsibility Principle,简称SRP )

核心思想:不要存在多于一个导致类变更的原因,通俗的说,即一个类只负责一个功能。如果一个类里面添加了多个功能,当其中有一个功能发生了变化,就会有多种原因引起这个类的变更,从而导致这个类的维护变得困难。在真实的开发中,不仅仅是类,函数也要遵循单一职责原则。即:一个函数负责一个功能。如果一个函数里面有不同的功能,则需要将不同的功能的函数分离出去。

优点:如果一个类或者一个函数的功能划分清晰,只负责某一项具体的功能,不但可以降低类或者函数的复杂度,而且还可以提高代码的可读性、应用程序的可维护性。

例如,需求上指出用一个类描述食肉和食草动物:

代码语言:javascript
复制
//================== Animal.h ==================

@interface Animal : NSObject

- (void)eatWithAnimalName:(NSString *)animalName;

@end

运行结果:

代码语言:javascript
复制
2018-10-27 17:55:25.775317+0800 DesignPatterns[54087:24701786] 狼 吃肉
2018-10-27 17:55:25.775689+0800 DesignPatterns[54087:24701786] 豹 吃肉
2018-10-27 17:55:25.775721+0800 DesignPatterns[54087:24701786] 虎 吃肉

上线后,发现问题了,并不是所有的动物都是吃肉的,比如羊就是吃草的。修改时如果遵循单一职责原则,需要将 Animal 类细分为食草动物类 Herbivore,食肉动物 Carnivore,代码如下:

代码语言:javascript
复制
//================== Herbivore.h ==================
@interface Herbivore : Animal

@end

@implementation Herbivore

- (void)eatWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃草", animalName);
}

@end

//================== Carnivore.h ==================
@interface Carnivore : Animal

@end

@implementation Carnivore

- (void)eatWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃肉", animalName);
}

@end

//================== main 函数 ==================
Animal *carnivore = [Carnivore new];
[carnivore eatWithAnimalName:@"狼"];
[carnivore eatWithAnimalName:@"豹"];
[carnivore eatWithAnimalName:@"虎"];
NSLog(@"\n");
Animal *herbivore = [Herbivore new];
[herbivore eatWithAnimalName:@"羊"];

在子类里面重写父类的 eatWithAnimalName 函数,运行结果:

代码语言:javascript
复制
2018-10-27 18:04:49.189722+0800 DesignPatterns[54422:24725132] 狼 吃肉
2018-10-27 18:04:49.190450+0800 DesignPatterns[54422:24725132] 豹 吃肉
2018-10-27 18:04:49.190482+0800 DesignPatterns[54422:24725132] 虎 吃肉
2018-10-27 18:04:49.190498+0800 DesignPatterns[54422:24725132] 
2018-10-27 18:04:49.190530+0800 DesignPatterns[54422:24725132] 羊 吃草

这样一来,不仅仅在此次新需求中满足了单一职责原则,以后如果还要增加食肉动物和食草动物的其他功能,就可以直接在这两个类里面添加即可。但是,有一点,修改花销是很大的,除了将原来的类分解之外,还需要修改 main 函数 。而直接修改类 Animal 来达成目的虽然违背了单一职责原则,但花销却小的多,代码如下:

代码语言:javascript
复制
//================== Animal.h ==================

@interface Animal : NSObject

- (void)eatWithAnimalName:(NSString *)animalName;

@end

@implementation Animal

- (void)eatWithAnimalName:(NSString *)animalName {
    if ([@"羊" isEqualToString:animalName]) {
        NSLog(@"%@ 吃草", animalName);
    } else {
        NSLog(@"%@ 吃肉", animalName);
    }
}

@end

//================== main 函数 ==================

Animal *animal = [Animal new];
[animal eatWithAnimalName:@"狼"];
[animal eatWithAnimalName:@"豹"];
[animal eatWithAnimalName:@"虎"];
[animal eatWithAnimalName:@"羊"];

运行结果:

代码语言:javascript
复制
2018-10-27 18:16:10.910397+0800 DesignPatterns[54677:24751636] 狼 吃肉
2018-10-27 18:16:10.911105+0800 DesignPatterns[54677:24751636] 豹 吃肉
2018-10-27 18:16:10.911138+0800 DesignPatterns[54677:24751636] 虎 吃肉
2018-10-27 18:16:10.911160+0800 DesignPatterns[54677:24751636] 羊 吃草

可以看到,这种修改方式要简单的多。 但是却存在着隐患:有一天需求上增加牛和马也需要吃草,则又需要修改 Animal 类的 eatWithAnimalName 函数,而对原有代码的修改会对调用狼、豹和虎吃肉等功能带来风险,也许某一天你会发现运行结果变为虎也吃草了。这种修改方式直接在代码级别上违背了单一职责原则,虽然修改起来最简单,但隐患却是最大的。还有一种修改方式:

代码语言:javascript
复制
//================== Animal.h ==================

@interface Animal : NSObject

/**
 *  吃草
 */
- (void)eatGrassWithAnimalName:(NSString *)animalName;

/**
 *  吃肉
 */
- (void)eatMeatWithAnimalName:(NSString *)animalName;

@end

@implementation Animal

- (void)eatGrassWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃草", animalName);
}

- (void)eatMeatWithAnimalName:(NSString *)animalName {
    NSLog(@"%@ 吃肉", animalName);
}

@end

//================== main 函数 ==================

Animal *animal = [Animal new];
[animal eatMeatWithAnimalName:@"狼"];
[animal eatMeatWithAnimalName:@"豹"];
[animal eatMeatWithAnimalName:@"虎"];
[animal eatGrassWithAnimalName:@"羊"];

运行结果:

代码语言:javascript
复制
2018-10-27 18:31:30.321473+0800 DesignPatterns[55048:24787008] 狼 吃肉
2018-10-27 18:31:30.321884+0800 DesignPatterns[55048:24787008] 豹 吃肉
2018-10-27 18:31:30.321922+0800 DesignPatterns[55048:24787008] 虎 吃肉
2018-10-27 18:31:30.321939+0800 DesignPatterns[55048:24787008] 羊 吃草

通过运行结果可以看到,这种修改方式没有改动原来的函数,而是在类中新加了一个函数,这样虽然也违背了类单一职责原则,但在函数级别上却是符合单一职责原则的,因为它并没有动原来函数的代码。

在实际的开发应用中,有很多复杂的场景,怎么设计一个类或者一个函数,让应用程序更加灵活,是更多程序员们值得思考的,需要结合特定的需求场景,有可能有些类里面有很多的功能,但是切记不要将不属于这个类本身的功能也强加进来,这样不仅带来不必要的维护成本,也违反了单一职责的设计原则

原则二、开闭原则(Open Close Principle,简称OCP)

它的定义是:程序中的对象应该对应扩展是开放的,对于修改是封闭的。

这个意思就是说,当一个类实现了一个功能的时候,如果想要改变这个功能不是去修改代码,而是通过扩展的方式去实现。实现该类提供的接口函数,然后注入到该类中,通过这种函数去实现功能的改变。

原则三、里氏替换原则(Liskov Substitution Principle,简称LSP)

它的定义是:所有引用基类的地方必须能透明的使用其子类对象。

说白了就是,只要父类能出现的地方子类就可以出现,替换为子类也不会产生任何的错误。开闭原则一般可以通过里氏替换实现对扩展开放,对修改关闭的效果。

原则四、依赖倒置原则(Dependence Inversion Principle,简称DIP)

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

原则五、接口隔离原则(Interface Segregation Principle,简称ISP)

它的定义是:客户端不应该依赖它不需要的接口。

它的目的是:解开系统的耦合,从而容易重构更改。

原则六、迪米特法则(Law of Demeter,简称LOD)

它的定义是:一个对象应该对其他对象有最少的了解

通俗的说,一个类应该对自己需要耦合或调用的类知道的越少越好,类的内部如何实现与调用者或依赖者没关系。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.10.27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原则一、单一职责原则(Single Responsibility Principle,简称SRP )
  • 原则二、开闭原则(Open Close Principle,简称OCP)
  • 原则三、里氏替换原则(Liskov Substitution Principle,简称LSP)
  • 原则四、依赖倒置原则(Dependence Inversion Principle,简称DIP)
  • 原则五、接口隔离原则(Interface Segregation Principle,简称ISP)
  • 原则六、迪米特法则(Law of Demeter,简称LOD)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档