前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >开发者都应该了解的SOLID原则(上)

开发者都应该了解的SOLID原则(上)

作者头像
AiTechYun
发布2019-06-21 16:54:14
4910
发布2019-06-21 16:54:14
举报
文章被收录于专栏:ATYUN订阅号

编译 | sunlei

发布 | ATYUN订阅号

面向对象的编程类型为软件开发带来了新的设计。

这使开发人员能够在一个类中组合具有相同目的/功能的数据,来实现单独的一个功能,不必关心整个应用程序如何。

但是,这种面向对象的编程还是会让开发者困惑或者写出来的程序可维护性不好。

为此,Robert C.Martin指定了五项指导方针。遵循这五项指导方针能让开发人员轻松写出可读性和可维护性高的程序

这五个原则被称为S.O.L.I.D原则(首字母缩写词由Michael Feathers派生)。

  • S:单一责任原则
  • O:开闭原则
  • L:里式替换
  • I:接口隔离
  • D:依赖反转

我们在下文会详细讨论它们。

笔记:本文的大多数例子可能不适合实际应用或不满足实际需求。这一切都取决于您自己的设计和用例。这都不重要,关键是您要了解明白这五项原则。

提示:SOLID原则旨在用于构建模块化、封装、可扩展和可组合组件的软件。Bit是一个帮助你践行这些原则的强大工具:它可以帮助您在团队中大规模地在不同项目中轻松隔离,共享和管理这些组件.来试试吧。

单一功能原则

Single Responsibilty Principle

一个类应该只负责一件事。如果一个类负责超过一件事,就会变得耦合。改功能的时候会影响另外一个功能。

  • 笔记:该原则不仅适用于类,还适用于软件组件和微服务。

举个例子,考虑这个设计:

代码语言:javascript
复制
1class Animal {
2    constructor(name: string){ }
3    getAnimalName() { }
4    saveAnimal(a: Animal) { }
5}

这个Animal类违反了SRP(单一责任原则)

怎么违反了呢?

SRP明确说明了类只能完成一项功能,这里,我们把两个功能都加上去了:animal数据管理和animal属性管理。构造函数和getAnimalName方法管理Animal的属性,然而,saveAnimal方法管理Animal的数据存储。

这种设计会给以后的开发维护带来什么问题?

如果app的更改会影响数据库的操作。必须会触及并重新编译使用Animal属性的类以使app的更改生效。

你会发现这样的系统缺乏弹性,像多米诺骨牌一样,更改一处会影响其他所有的地方。

让我们遵循SRP原则,我们创建了另外一个用于数据操作的类:

代码语言:javascript
复制
1class Animal {
2    constructor(name: string){ }
3    getAnimalName() { }
4}
5class AnimalDB {
6    getAnimal(a: Animal) { }
7    saveAnimal(a: Animal) { }
8}

“我们在设计类时,我们应该把相关的功能放在一起,所以当他们需要发生改变时,他们会因为同样的原因而改变。如果是因为不同的原因需要改变它们,我们应该尝试把它们分开。” – Steven Fenton

遵循这些原则让我们的app变得高内聚。

开闭原则

Open-Closed Principle

软件实体(类,模块,函数)应该是可以扩展的,而不是修改。

继续看我们的Animal类

代码语言:javascript
复制
1class Animal {
2    constructor(name: string){ }
3    getAnimalName() { }
4}

我们想要遍历动物列表并且设置它们的声音。

代码语言:javascript
复制
 1//...
 2const animals: Array<Animal> = [
 3    new Animal('lion'),
 4    new Animal('mouse')
 5];
 6function AnimalSound(a: Array<Animal>) {
 7    for(int i = 0; i <= a.length; i++) {
 8        if(a[i].name == 'lion')
 9            log('roar');
10        if(a[i].name == 'mouse')
11            log('squeak');
12    }
13}
14AnimalSound(animals);

AnimalSound函数并不符合开闭原则,因为一旦有新动物出现,它需要修改代码。

如果我们加一条蛇进去:

代码语言:javascript
复制
1//...
2const animals: Array<Animal> = [
3    new Animal('lion'),
4    new Animal('mouse'),
5    new Animal('snake')
6]
7//...

我们不得不改变AnimalSound函数:

代码语言:javascript
复制
 1//...
 2function AnimalSound(a: Array<Animal>) {
 3    for(int i = 0; i <= a.length; i++) {
 4        if(a[i].name == 'lion')
 5            log('roar');
 6        if(a[i].name == 'mouse')
 7            log('squeak');
 8        if(a[i].name == 'snake')
 9            log('hiss');
10    }
11}
12AnimalSound(animals);

每当新的动物加入,AnimalSound函数就需要加新的逻辑。这是个很简单的例子。当你的app变得庞大和复杂时,你会发现每次加新动物的时候就会加一条if语句,随后你的app和AnimalSound函数都是if语句的身影。

那怎么修改AnimalSound函数呢?

代码语言:javascript
复制
 1class Animal {
 2        makeSound();
 3        //...
 4}
 5class Lion extends Animal {
 6    makeSound() {
 7        return 'roar';
 8    }
 9}
10class Squirrel extends Animal {
11    makeSound() {
12        return 'squeak';
13    }
14}
15class Snake extends Animal {
16    makeSound() {
17        return 'hiss';
18    }
19}
20//...
21function AnimalSound(a: Array<Animal>) {
22    for(int i = 0; i <= a.length; i++) {
23        log(a[i].makeSound());
24    }
25}
26AnimalSound(animals);

现在Animal有个makeSound的私有方法。我们每一个animal继承了Animal类并且实现了私有方法makeSound。

每个animal实例都会在makeSound中添加自己的实现方式。AnimalSound方法遍历animal数组并调用其makeSound方法。

现在,如果我们添加了新动物,AnimalSound方法不需要改变。我们需要做的就是添加新动物到动物数组。

AnimalSound方法现在遵循了开闭原则。

另一个例子:

假设您有一个商店,并且您使用此类给您喜爱的客户打2折:

代码语言:javascript
复制
1class Discount {
2    giveDiscount() {
3        return this.price * 0.2
4    }
5}

当您决定为VIP客户提供的折扣翻倍。 您可以像这样修改类:

代码语言:javascript
复制
 1class Discount {
 2    giveDiscount() {
 3        if(this.customer == 'fav') {
 4            return this.price * 0.2;
 5        }
 6        if(this.customer == 'vip') {
 7            return this.price * 0.4;
 8        }
 9    }
10}

哈哈哈,这样不就背离开闭原则了么?如果我们又想加新的折扣,那又是一堆if语句。

为了遵循开闭原则,我们创建了继承Discount的新类。在这个新类中,我们将会实现新的行为:

代码语言:javascript
复制
 1class VIPDiscount: Discount {
 2    getDiscount() {
 3        return super.getDiscount() * 2;
 4    }
 5}
 6如果你决定给VIP80%的折扣,就像这样:
 7
 8class SuperVIPDiscount: VIPDiscount {
 9    getDiscount() {
10        return super.getDiscount() * 2;
11    }
12}

你看,这不就不用改了。

今天get到新的收获了吗?还有一部分内容,我接下来会更新,期待吧。

End

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ATYUN订阅号 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单一功能原则
  • Single Responsibilty Principle
  • 开闭原则
  • Open-Closed Principle
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档