js 的七大原则--单一原则、开闭原则、替换原则(一)

一.前言:

js 的七大设计原则:

1.单一原则

2.开闭原则

3.里氏替换原则

4.依赖倒转原则

5.接口隔离原则

6.合成复用原则

7.迪米尔法则

二.单一原则

1.定义:单一原则就是一个对象或者一个方法,只做一件事。

比如,目前公司的前端框架,如下图:在src中api只是做接口层,assets里面是公共的方法,components是用来放组件的,里面的base和business分别存放的是基础组件和业务组件,mixins是用来存放混入的东西的。store文件时用来放vuex中的东西的,style文件是用来存放样式的。每个文件都有各自的职责,也都只负责一件事情。这就符合单一职责的。

遵循单一职责的优点:

1.可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多。

2.提高类的可读性,提高系统的可维护性。

3.变更引起的风险降低,变更时必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。

三.开闭原则

尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化。

一个软件产品的生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候,尽量的适应这些变化。以提高项目的稳定性和灵活性。

四.里氏替换原则

严格的定义:如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序p在所有的对象o1都换成o2的时候,程序p的行为没有变化,那么类型T2就是类型T1的子类型。

通俗的定义:所有引用基类的地方必须能透明地使用其子类的功能。

更通俗的定义:子类可以扩展父类的功能,但是不能改变父类原有的功能。

首先来看一个例子,看它是否满足“里氏替换”的原则

//定义一个矩形类

class
Rectangle { constructor() { this.width=0; this.height=0; } setWidth(width) { this.width = width } setHeight(height) { this.height = height } getArea() { return this.width * this.height } }

//定义一个正方形类,继承于矩形类
class Square extends Rectangle { constructor() { super(); } setWidth(width) { this.width = width; this.height = width; } setHeight(height) { this.height = height this.width = height } }
// 执行的方法 function result(rectangles) { rectangles.forEach((rectangle)
=> { rectangle.setHeight(5) rectangle.setWidth(4) let area = rectangle.getArea() console.log('area', area) }) } let rectangles = [new Rectangle(), new Rectangle(), new Square()]; result(rectangles) //结果是20 ,20, 16

在我当初看到这个代码的时候,我的疑惑点在于为什么正方形求面积是16。其实,仔细看一下上面的代码,我们会发现,其实他是没有遵循“里氏替换”的原则的。因为正方形的类继承了矩形的类,但是它在实现的时候改写了矩形的类。这样子,如果我们将子类替换成父类的时候,结果就是不成功的。所以,里氏替换原则包含以下4层含义:

1.子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象的方法。

2.子类可以增加自己独有的方法

3.当子类覆盖或者实现父类的时候,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。

4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

上面的例子,在正方形的类中的setHeight方法不仅仅改变了height,还改变了width,当功能扩展的时候,子类尽量不要去重写父类的方法,而是另写一个方法。

五.依赖倒置原则

定义:高层模块不应该依赖于 低层模块,二者都应该依赖其抽象;抽象不应该依赖于细节,细节应该依赖于抽象。

怎么理解上面的话呢?

类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。

在这里面类A就相当于高层模块,类B和类C就相当于低层模块。所以现在的业务逻辑是不满足“依赖倒置”原则的。为了满足“依赖倒置”的原则,其实,我们应该有一个抽象类o,然后类b,类c是o的实现,然后类A通过o简介与类B,类c 创建联系,这样,就大大降低了修改类A的几率了。

依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在Java中,抽象指的是接口或者抽象类,细节就是具体的实现类。使用接口或者抽象类的目的是制定好规范和契约,而不去设计任何具体的操作,把展现细节的任务交给她们的实现类去完成。

六.接口隔离原则

每一个接口都是一个角色,客户端不应该依赖于他不需要的接口。也就是说,一个类对另一个类的依赖应该建立在最小的接口上。

应该把每一个接口都细化,针对类去设计接口。如果一个接口中有 太多的方法,而对很多类来说里面的很多方法都是用不到的,那么,另外的类在实现这个接口时就要实现很多对它来说没用的方法,浪费人力物力。对一个类来说,实现很多它都能用得上的专用接口总比让它实现一个臃肿而又有很多它用不上的方法要来的划算。

采用接口隔离的原则,需要注意以下几点:

1.接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不争的事实,但是,如果过小,则会造成接口数量过多,使得设计复杂化。所以,一定要适度。

2.为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制的服务,才能建立最小的依赖关系。

3.提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

4.运用接口隔离原则,一定要适度。接口设计过大或过小都不好。

七.合成/聚合复用原则

聚合表示一种弱的“拥有”的关系,体现的是A对象可以包含B对象,但是B对象不是A对象的一部分;合成则是一种强的“拥有”的关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。

优先使用对象的合成/聚合将有助于保持每个类的封装,并被几种在单个任务上。这样类和类继承层次会保持较小的规模,并且不太可能增长到不可控制的庞然大物上。

八.迪米特法则

也叫最小知识原则,一个对象应该对其他对象有尽可能少的了解。

总结:

单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。

 

posted @ 2019-08-29 17:56  栀子花编织着留恋  阅读(1561)  评论(0编辑  收藏  举报