Js面向对象之七大基本原则小白入门_原则菜鸟教程

面向对象编程有自己的特性与原则,如果对于面向对象有一些了解的话,面向对象三大特征,封装、继承、多态单一职责如果我们在编写程序的时候,一类或者一个方法里面包含了太多方法,对于代码的可读性来说,无非是一场灾难,对于我们来说。所以为了解决这个问题,出现了单一职责。什么是单一职责 ? 单一职责:又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应

Js面向对象之七大基本原则小白入门

面向对象编程有自己的特性与原则,如果对于面向对象有一些了解的话,面向对象三大特征,封装、继承、多态

Js面向对象之七大基本原则小白入门_原则菜鸟教程

单一职责

如果我们在编写程序的时候,一类或者一个方法里面包含了太多方法,对于代码的可读性来说,无非是一场灾难,对于我们来说。所以为了解决这个问题,出现了单一职责。

什么是单一职责 ? 

单一职责:又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。(节选自百度百科)

按照上面说的,就是对一个类而言,应该仅有一个引起它变化的原因。换句话说,一个类的功能要单一,只做与它相关的事情。在类的设计过程中要按职责进行设计,彼此保持正交,互不干涉。

单一职责的好处  

  1. 类的复杂性降低,实现什么职责都有清晰明确的定义
  2. 可读性提高,复杂性降低,那当然可读性提高了
  3. 可维护性提高,可读性提高,那当然更容易维护了
  4. 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。

实例  

class ShoppinCar {
    constructor(){
        this.goods = [];
    }
    addGoods(good){
        this.goods = [good];
    }
    getGoodsList(){
        return this.goods;
    }
}
class Settlement {
    constructor(){
        this.result = 0; 
    }
    calculatePrice(list,key){
        let allPrice = 0;
        list.forEach((el) => {
            allPrice += el[key];
        })
        this.result = allPrice;
    }
    getAllPrice(){
        return this.result;
    }
}

用上面的代码来说ShoppinCar类存在两个方法addGoods和getGoodsList,分别是添加商品和获取商品列表。Settlement类中存在两个方法calculatePrice和getAllPrice分别做的事情是计算价钱与获取总价钱。ShoppinCar与Settlement都是在做自己的事情。添加商品与计算价格,虽然在业务上是相互依赖的,但是在代码中分别用两个类,然他们自己做自己的事情。其中任何一个类更改不会对另一个类进行更改。

开闭原则

在一个类中暴露出去的方法,若这个方法变更了,则会产生很大的后果,可能导致其他依赖于这个方法且有不需要变更的业务造成大面积瘫痪。为了解决这个问题,可以单独再写一个方法,若这个方法与这个类中的其他方法相互依赖。

解决办法:

  1. 把其中依赖的代码copy一份到新的类中。
  2. 在新类中引用旧类中的方法。

两种方法都不是最好的解决方案。

第一种方法会导致代码大量的重复,第二种方法会导致类与类之间互相依赖。

什么是开闭原则  

开闭原则:“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。(节选自百度百科)

开闭原则对扩展开放,对修改关闭,并不意味着不做任何修改,底层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。开闭原则是一个最基本的原则,另外六个原则都是开闭原则的具体形态,是指导设计的工具和方法,而开闭原则才是精神领袖.

开闭原则好处  

  1. 开闭原则有利于进行单元测试
  2. 开闭原则可以提高复用性
  3. 开闭原则可以提高可维护性
  4. 面向对象开发的要求

实例  

class Drag {
    down(){
        //  ...
    }   
    move(){
        //  ...
        // 对拖拽没有做任何限制可以随意拖拽
    }   
    up(){
        //  ...
    }  
}
class LimitDrag extends Drag {
    move(){
        //  ...
        //  重写该方法对拖拽进行限制处理
    }
}

在LimitDrag中重写了move方法,若修改了可以满足两种需求,一种是限制型拖拽,一种是不限制型拖拽,任何一个更改了另外一个还是可以正常运行。

里氏替换

每个开发人员在使用别人的组件时,只需知道组件的对外裸露的接口,那就是它全部行为的集合,至于内部到底是怎么实现的,无法知道,也无须知道。所以,对于使用者而言,它只能通过接口实现自己的预期,如果组件接口提供的行为与使用者的预期不符,错误便产生了。里氏替换原则就是在设计时避免出现派生类与基类不一致的行为。

什么是里氏替换  

里氏替换原则:OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。(节选自百度百科)

里氏替换好处  

  1. 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性
  2. 提高代码的重用性
  3. 子类可以形似父类,但是又异于父类。
  4. 提高代码的可扩展性,实现父类的方法就可以了。许多开源框架的扩展接口都是通过继承父类来完成。
  5. 提高产品或项目的开放性

实例  

//  抽象枪类
class AbstractGun {
    shoot(){
        throw "Abstract methods cannot be called";
    }
}
//  步枪
class Rifle extends AbstractGun {
    shoot(){
        console.log("步枪射击...");
    }
}
//  狙击枪
class AUG extends Rifle {
    zoomOut(){
        console.log("通过放大镜观察");
    }
    shoot(){
        console.log("AUG射击...");
    }
}
//  士兵
class Soldier {
    constructor(){
        this.gun = null;
    }
    setGun(gun){
        this.gun = gun;
    }
    killEnemy(){
        if(!this.gun){
            throw "需要给我一把枪";
            return;
        }
        console.log("士兵开始射击...");
        this.gun.shoot();
    }
}
//  狙击手
class Snipper extends Soldier {
    killEnemy(aug){
        if(!this.gun){
            throw "需要给我一把枪";
            return;
        }
        this.gun.zoomOut();
        this.gun.shoot();
    }
}
let soldier = new Soldier();
soldier.setGun(new Rifle());
soldier.killEnemy();

let snipper = new Snipper();
//  分配狙击枪
snipper.setGun(new AUG());
snipper.killEnemy();

snipper.setGun(new Rifle());
// snipper.killEnemy();  //  this.gun.zoomOut is not a function

从上述代码中可以看出,子类和父类之间关系,子类方法一定是等于或大于父类的方法。子类能够出现的父类不一定能出现,但是父类出现的地方子类一定能够出现。

依赖倒置

如果方法与方法之间或类与类之间,存在太多的依赖关系会导致代码可读性以及可维护性很差。依赖倒置原则能够很好的解决这些问题。

什么是依赖倒置  

依赖倒置原则:程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。(节选自百度百科)

  1. 高层模块不应该依赖低层模块,两者都应该依赖其抽象
  2. 抽象不应该依赖细节
  3. 细节应该依赖抽象

依赖倒置好处  

  1. 通过依赖于接口,隔离了具体实现类
  2. 低一层的变动并不会导致高一层的变动
  3. 提高了代码的容错性、扩展性和易于维护

实例  

//  抽象枪类
class AbstractGun {
    shoot(){
        throw "Abstract methods cannot be called";
    }
}
//  步枪
class Rifle extends AbstractGun {
    shoot(){
        console.log("步枪射击...");
    }
}
//  狙击枪
class AUG extends AbstractGun {
    shoot(){
        console.log("AUG射击...");
    }
}

从上面的代码可以看出,步枪与狙击枪的shoot全部都是依赖于AbstractGun抽象的枪类,上述编程满足了依赖倒置原则。

接口隔离

什么是接口隔离  

接口隔离:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。(节选自百度百科)

接口隔离原则与单一职责原则的审视角度不相同。单一职责原则要求是类和接口的职责单一,注重的职责,这是业务逻辑上的划分。接口隔离原则要求接口的方法尽量少。

接口隔离好处  

  1. 避免接口污染
  2. 提高灵活性
  3. 提供定制服务
  4. 实现高内聚

实例  

function mix(...mixins) {
  class Mix {}
  for (let mixin of mixins) {
    copyProperties(Mix, mixin);
    copyProperties(Mix.prototype, mixin.prototype);
  }
  return Mix;
}
function copyProperties(target, source) {
  for (let key of Reflect.ownKeys(source)) {
    if ( key !== "constructor"&& key !== "prototype"&& key !== "name") {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      Object.defineProperty(target, key, desc);
    }
  }
}
class Behavior {
    eat(){
        throw "Abstract methods cannot be used";
    }   
    call(){
        throw "Abstract methods cannot be used";
    }
}
class Action {
    climbTree(){
        throw "Abstract methods cannot be used";
    }
}
class Dog extends Behavior{
    eat(food){
        console.log(`狗正在吃${food}`);
    }
    hungry(){
        console.log("汪汪汪,我饿了")
    }
}
const CatMin = mix(Behavior,Action);
class Cat extends CatMin{
    eat(food){
        console.log(`猫正在吃${food}`);
    }
    hungry(){
        console.log("喵喵喵,我饿了")
    }
    climbTree(){
        console.log("爬树很开心哦~")
    }
}
let dog = new Dog();
dog.eat("骨头");
dog.hungry();
let cat = new Cat();
cat.eat("鱼");
cat.hungry();
cat.climbTree();

大家一定要好好分析一下上面的代码,共有两个抽象类,分别对应不同的行为,Cat与Dog类拥有共同的行为,但是Cat又拥有其自己单独的行为,使用抽象(即接口)继承其方法,使用接口隔离使其完成各自的工作,各司其职。

迪米特法则

迪米特法则:最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD.(节选自百度百科)

迪米特法则的做法观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用率才可以提高。一个类应该对其他对象保持最少的了解。通俗来讲,就是一个类对自己依赖的类知道的越少越好。因为类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

迪米特法则好处  

  1. 减少对象之间的耦合性

实例  

class ISystem {
    close(){
        throw "Abstract methods cannot be used";
    }
}
class System extends ISystem{
    saveCurrentTask(){
        console.log("saveCurrentTask")
    }
    closeService(){
        console.log("closeService")
    }
    closeScreen(){
        console.log("closeScreen")
    }
    closePower(){
        console.log("closePower")
    }
    close(){
        this.saveCurrentTask();
        this.closeService();
        this.closeScreen();
        this.closePower();
    }
}
class IContainer{
    sendCloseCommand(){
        throw "Abstract methods cannot be used";
    }
}
class Container extends IContainer{
    constructor(){
        super()
        this.system = new System();
    }
    sendCloseCommand(){
        this.system.close();
    }
}
class Person extends IContainer{
    constructor(){
        super();
        this.container = new Container();
    }
    clickCloseButton(){
       this.container.sendCloseCommand();
    }
}
let person = new Person();
person.clickCloseButton();

上面代码中Container作为媒介,其调用类不知道其内部是如何实现,用户去触发按钮,Container把消息通知给计算机,计算机去执行相对应的命令。

组合/聚合复用原则

聚合(Aggregation)表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象但B对象不是A对象的一部分。

合成(Composition)则是一种强的’拥有’关系,体现了严格的部分和整体关系,部分和整体的生命周期一样。

组合/聚合:是通过获得其他对象的引用,在运行时刻动态定义的,也就是在一个对象中保存其他对象的属性,这种方式要求对象有良好定义的接口,并且这个接口也不经常发生改变,而且对象只能通过接口来访问,这样我们并不破坏封装性,所以只要类型一致,运行时还可以通过一个对象替换另外一个对象。

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

组合/聚合复用原则好处  

  1. 新的实现较为容易,因为超类的大部分功能可通过继承关系自动进入子类;
  2. 修改或扩展继承而来的实现较为容易

实例  

function mix(...mixins) {
  class Mix {}
  for (let mixin of mixins) {
    copyProperties(Mix, mixin);
    copyProperties(Mix.prototype, mixin.prototype);
  }
  return Mix;
}
function copyProperties(target, source) {
  for (let key of Reflect.ownKeys(source)) {
    if ( key !== "constructor"&& key !== "prototype"&& key !== "name") {
      let desc = Object.getOwnPropertyDescriptor(source, key);
      Object.defineProperty(target, key, desc);
    }
  }
}
class Savings {
    saveMoney(){
        console.log("存钱");
    }
    withdrawMoney(){
        console.log("取钱");
    }
}
class Credit {
    overdraft(){
        console.log("透支")
    }
}
const CarMin = mix(Savings,Credit);
class UserCar extends CarMin {
    constructor(num,carUserName){
        super();
        console.log()
        this.carNum = num;
        this.carUserName = carUserName;
    }
    getCarNum(){
        return this.carNum;
    }
    getCarUserName(){
        return this.carUserName;
    }
}
let myCar = new UserCar(123456798,"Aaron");
console.log(myCar.getCarNum());
console.log(myCar.getCarUserName());
myCar.saveMoney();
myCar.withdrawMoney();
myCar.overdraft();

总结

这些原则在设计模式中体现的淋淋尽致,设计模式就是实现了这些原则,从而达到了代码复用、增强了系统的扩展性。所以设计模式被很多人奉为经典。我们可以通过好好的研究设计模式,来慢慢的体会这些设计原则。

海计划公众号
(0)
上一篇 2020/03/26 23:32
下一篇 2020/03/26 23:31

您可能感兴趣的内容

  • javascript对dom的操作总汇,js创建,更新,添加,删除DOM的方法入门指南_js知识小白常识

    HTML文档在浏览器解析后,会成为一种树形结构,我们需要改变它的结构,就需要通过js来对dom节点进行操作。dom节点(Node)通常对应的是一个标题,文本,或者html属性,都有一个nodeType来标示他的类型:值 nodeType名称 1 Element 元素 2 Attribute 属性 3 Text 文本nodeType表示Node的类型,它是一

    2020/04/06
  • JSXGraph使用攻略一个支持各种浏览器的交互式几何图库绘制

    JSXGraph基础入门 官方网址:http://jsxgraph.org GitHub:https://github.com/jsxgraph/jsxgraph 简介描述:一个支…

    2020/03/05
  • Canvas在移动端绘制模糊的原因与解决办法入门攻略_canvas小白攻略

    由于一些移动端的兼容性原因,我们某个项目需要前端将pdf转换成在移动端页面可直接观看的界面。为了方便解决,我们采用了pdf.js这个插件,该插件可以将pdf转换成canvas绘制在页面上。不过,在测试过程中却发现,在移动端的浏览器上,绘制的内容展示十分模糊(如下图),经过分析之后发现是由于移动端高清屏幕引起的。 在解决问题之后以文字方式记述原因和探究结果在解

    2020/03/30
  • JavaScript中的类型转换小白指南_类型指南教程

    正文之前,先抛出几组问题:// 第一组
    [] == [] //false// 第二组
    [] == ![] //true{} == !{} //false{} == ![] // false[] == !{} //true{} == 1 // false// 第三组
    {} 1 // false看到这几个问题,是不是一脸懵逼?稍

    2020/03/26
  • vue的provide / inject 有什么用?小白攻略_组件小白攻略

    1.前言vue的父子组件通信用什么?:prop和$emit的组合。如果是爷孙组件呢?:那么就要用父组件来转发数据和事件了。如果是太爷爷和孙子组件呢?:当然是vuex啦emmm 好的,没我啥事了,我这就走。不行,我还能再挣扎一会儿!肯定有一部分兄弟做的项目比较小,组件通信的情况不是很多,懒得引入vuex,那么provide/inject就是爷孙(不限于爷孙/父

    2020/03/23
  • CSS :placeholder-shown伪类实现输入框浮动文字效果小白知识_效果入门攻略

    在这篇文章中,我们将使用:placeholder-shown伪类创建一个浮动的问题标签效果,使用纯CSS实现。浮动的文字标签当我们处理输入框时,会想方设法提供给用户更好的体验。有两个标签属性是我们经常会用到的:label标签是关联表单元素,提供说明信息最适合的元素。输入框的placeholder属性允许您指定没有输入内容时出现在元素内的文本。它

    2020/03/29
  • 学好前端的 6 点建议指南教程_前端使用指南

    最近接触了很多前端的小伙伴,和他们谈了很多职业发展的问题。他们大部分是做了一到三年的前端新手。在交流中我发现了一个很有意思的现象,大家同样是入门不足三年,一部分感觉前端是一个很有前途的职业,甚至一部分两年经验的前端同学透露年薪已经30W以上了,而另一部分则表示前端薪资水平不高,技术体系庞杂,迭代速度飞快,苦于学习各种不断更新的技术和框架,心神俱疲。同样是前端

    2020/03/31
  • vue watch监听对象的使用小白入门实现首次不触发、深度监听_vue基础知识教程

    vue中的watch是一个对象,所以一定要当成对象来用,它有键-值组成,其中键就是你要监控的那个数据,比如说$route,这个就是要监控路由的变化。或者是data中的某个变量。 值可以是函数:就是当你监控的数据变化时需要执行的函数,这个函数有两个形参,第一个是当前值,第二个是变化后的值。 值也可以是函数名:不过这个函数名要用单引号来包裹。 值也可以是包括选

    2020/04/05
  • 如何写出优美的javascript代码?小白常识_代码入门基础教程

    在多年以前,人们注重功能是如何实现的。现如今,随着Web及互联网技术的不断发展,功能仅成了最基本的要求,如何写出漂亮,整洁的代码已成为一个大牛级程序员不可或缺的条件。写出优美的JavaScript代码的方法:少写代码提前设计能有助于少写代码,增强全局感。而代码写得少还能防止失控——感觉不对时就应该停下来,腾出时间来思考,为什么会偏离最先的想法。遵行惯用法注释

    2020/03/22
  • jquery图片懒加载菜鸟教程网_加载使用帮助

    对于一个有多个图片的网站来说,访问的时候不应该直接加载所有图片,而是应该只将浏览器窗口内的图片进行加载。当滚动的时候,在加载更多的图片,叫做图片的懒加载。我们可以通过给img自定义一个新属性,来存储图片真实的src地址,当需要加载的时候,再将这个真实的地址赋给src,进行图片加载。整体思路:1、设置个data-src(自定义一个属性)来存放真实地址2、当滚动

    2020/03/23
  • js语言中常见错误总汇小白指南_js技巧基础入门

    1. Uncaught TypeError: Cannot Read Property这是 JavaScript 开发人员最常遇到的错误。当你读取一个属性或调用一个未定义对象的方法时,Chrome 中就会报出这样的错误。导致这个错误发生的原因有很多,常见的一种情况是在渲染 UI 组件时,不正确地初始化状态。2. TypeError: ‘undefined

    2020/04/03
  • hilo新手入门阿里开发的一款HTML5跨终端游戏解决方案

    hilo基础入门 官方网址:http://hiloteam.github.io/ GitHub:https://github.com/hiloteam/Hilo 简介描述:阿里开发…

    2020/03/05
  • 《深入浅出webpack》有感小白入门_webpack使用教程

    对于前端仔来说,相信大家对webpack都再熟悉不过了,但是你对webpack的了解程度又有多深呢,笔者花了几天时间看了一下《深入浅出webpack》,虽然说书中大部分介绍的是配置和使用相关的,但是如果你对webpack的配置、使用、原理和构建流程更加熟悉的话,对于你的开发可以说是百里无一害!本文不会局限于介绍配置,也不会详细介绍打包原理(后面打算写一篇有关

    2020/03/23
  • git合并分支菜鸟知识_git基础入门

    git合并分支菜鸟知识 假如我们现在在dev分支上,刚开发完项目,执行了下列命令: git add . git commit -m ‘提交的备注信息’ git push -u or…

    2020/03/19
  • 7个带陷阱的js面试题入门百科_面试基础知识

    在 JS 面试中,经常会看到一些简单而又沙雕的题目,这些题目包含一些陷阱,但这些在我们规范的编码下或者业务中基本不会出现。 有些面试官就是这样,不专注于制定代码的标准和规范上,却用不规范的代码去检验别人是否细心。这魔幻的世界就是一个攀比优越感的,我能考你,我就是比你优越,真实。来看看这 7 个沙雕题目是哪些。1. 偶然创建的全局变量面试官问在下面的代码中 t

    2020/03/24
  • 14个 JavaScript 中鲜为人知的技巧基础知识入门_技巧入门基础知识

    人们通常认为 JavaScript 是一门很容易上手的语言,但是要做到精通却不简单。是的,这是因为 JavaScript 是一种非常古老且非常灵活的语言。它充满了神秘的语法和过时的功能。到目前为止,我已经使用 JavaScript 多年了,但是还是时不时就会发现一些我不知道的隐藏语法或技巧。我试图列出一些鲜为人知的JavaScript功能。尽管其中一些功能在

    2020/03/21