《Javascript设计模式核心原理与应用实践》笔记整理

关于设计模式, 不论是静态类型语言,仍是像JavaScript这样的动态类型语言,都常常出现,这些设计模式,万变不离其宗,重要的是理念,而不是概念,全部的模式都是前人的总结,重点不是怎么去把这些模式硬塞到项目中,而是学会如何在恰当的位置,以事半功倍的方式去使用他们。javascript

1、设计原则

设计模式基于的原则SOLID:vue

1. 单一功能原则-Single Responsibility Principlejava

2. 开放封闭原则 - Opened Closed Principlevuex

3. 里式替换原则 - Liskov Substitution Principleredux

4. 接口隔离原则 - Interface Segregation Principle设计模式

5. 依赖反转原则 - Dependency Inversion Principle闭包

看完并非这些都是什么概念。。。嗯,接着往下学。app

设计模式的核心是-封装变化。iphone

意思是: 将变与不变隔离开,不变的稳定下来,变化的部分封装起来,确保迭代的时候改动最小。函数


23种设计模式,看的眼花,基本记不住。

建立型、结构型、行为型。大体分为这三种。

2、建立型1 - 工厂模式

构造函数

function Person(name, age, gender) {
    this.name = name
    this.age = age
    this.gender = gender
}复制代码

上面的例子是构造函数,调用它的方式:

const person1 = new Person('张三', 24, '男')复制代码

构造器模式就是用构造函数Person来初始化对象person1。

能够发现,变的是person的属性值,不变的是这些共性的属性。

构造器模式是抽象了对象实例的变与不变。

工厂模式: 抽象不一样构造函数之间的变与不变。

简单工厂模式

上面的例子里,稳定的是person的属性,可是若是要添加新的属性,给不一样的性别描述,就要往构造函数中手动添加,那么这个构造函数也变的相对不稳定了。

这时候,要把这个变也抽出来。

function Man(name, age) {
    this.name = name
    this.age = age
    this.gender = '男'
    this.genderDesc = '孔武有力'
}

function Woman(name, age) {
    this.name = name
    this.age = age
    this.gender = '女'
    this.genderDesc = '温柔似水'
}

function factory(name, age, gender) {
    switch (gender) {
	case '男':
	    return new Man(name, age)
	    break
	case '女':
	    return new Woman(name, age)
	    break
    }
}复制代码

这只是性别的描述,只判断了两个,若是是别的,职业、爱好...要添加在factory中的判断就不少不少。

在上面的例子中,无论男女,都有name、age、gender、genderDesc, 值都是变的,genderDesc随着gender的变化而变化。

function Person(name, age, gender, genderDesc) {
    this.name = name
    this.age = age
    this.gender = gender
    this.genderDesc = genderDesc
}

function factory(name, age, gender) {  
    let genderDesc  
    switch (gender) {    
        case '男':
            genderDesc = '孔武有力'
            break
        case '女':      
            genderDesc = '温柔如水'      
            break  
    }  
    return new Person(name, age, gender, genderDesc)
}复制代码

这样写好像好多了。

这就是工厂模式:

将建立对象的过程单独封装,目的就是为了无脑传参。

构造器是解决了不断实例的问题,工厂模式解决了多个类的问题。

3、建立型2- 抽象工厂模式

什么叫抽象类,java这种强类型的静态语言中,会在编译阶段就对类型进行检查,定义的是什么类型,若是值不对,编译的时候直接就抛出异常了,建立对象时,要关注类型之间的解耦,来保证多态性,然而在JavaScript中,自然就具备多态性,看起来好像不须要抽象类。

上面那个例子,若是是对不一样的工做内容进行抽取判断,不一样的工做岗位,权限也是不同的,好比管理岗,外包,等,每次新加一个职业群体,就得在factory中加一个判断,会越加越多,维护起来也很复杂。

开放封闭原则: 对拓展开放,对修改封闭。意思是,实体(类、模块、函数)能够扩展,可是不能够修改。

不停的添加判断就是在修改,不是在扩展。

下面一个例子: 

手机最重要的是操做系统和硬件两部分,手机厂商量产手机时能够建立以下类:

class MobileFactory {
    // 操做系统接口
    createOS(){
        throw new Error("抽象工厂方法不容许直接调用,你须要将我重写!")
    }
    // 硬件接口
    createHardWare(){
        throw new Error('抽象工厂方法不容许直接调用, 你须要将我重写')
    }
}复制代码

上面的类除了声明量产手机的通用线, 定下规矩 ,啥也不干,不能够直接调用。在抽象工厂里,这个类就是食物链顶端的抽象工厂AbstractFactory。

抽象工厂不干活,干活是具体工厂的事情(ConcreteFactory)。当明确一条线要生产什么手机,具体工厂的工做就明确了,若是如今要生产一个Android+高通硬件的手机,名为FakeStar,就能够为FakeStar建立一个具体工厂:

class FakeStarFactory extends MobileFactory{
    createOS(){
        // 安卓系统实例
        return new AndroidOS()
    }
    createHardWare(){
        // 高通硬件实例
        return new QualcommHardWare()
    }
}复制代码

此处咱们调用了两个构造函数AndroidOS和QualcommHardWare。像这样的用来new出具体对象的类,叫具体产品类-ConcreteProduct。这种类每每不会孤立存在,不一样具体产品每每会有相同的功能,安卓和iOS都有操做系统和硬件系统,所以咱们能够创造一个抽象产品类-AbstractProduct,来声明这一类产品应该具备的基本功能。

// 定义操做系统这类产品的抽象产品类
class OS {
    controlHardWare(){
        throw new Error('我不容许被直接调用')
    }
}

// 定义具体操做系统的具体实现类
class AndroidOS extends OS {
    controlHardWare() {
        console.log('我会用安卓的方式操做硬件')
    }
}

class AppleOS extends OS {
    controlHardWare(){
        console.log('我会用苹果的方式操做硬件')
    }
}复制代码

硬件类产品同理

// 定义硬件这类产品的抽象产品类
class HardWare {
    // 手机硬件产品的共性方法,这里是根据命令运转
    operateByOder() {
        thew new Error('我不容许被直接调用')
    }
}
// 定义具体硬件的具体实现类
class QualcommHardWare extends Hardware {
    operateByOrder(){
        console.log('用高通方式运转')
    }
}

class MiHardWare extends HardWare {
    opreateByOrder(){
        console.log('用小米方式运转')
    }
}复制代码

这样一来,咱们生产一部FakeStar,只须要这样

// 手机
const iphone6 = new FakeStarFactory()
// 手机的操做系统
const myOS = iphone.createOS()
// 手机的硬件
const myHardWare = iphone.createHardWare()
// 启动操做系统
myOS.controlHardWare()
// 唤醒硬件
myHardWare.operateByOrder()复制代码

FakeStar过气了,再生产别的手机,咱们也不须要对MobileFactory作任何修改,再新建一个手机种类就好了。

class FakeProFactory extends MobileFactory {
    createOS(){
        return new ...
    }
    createHardWard(){
        return new ...
    }
}复制代码

这样他不会对现有的模块形成任何影响。

总结简单工厂模式和抽象工厂模式的异同:

他们都将同与不一样分离,场景复杂度不一样而已。

简单工厂模式里,处理的对象是类,逻辑自己比较简单,不苛求可扩展性。

抽象工厂模式里,处理对象也是类,可是类比较复杂,类中分出了品种和等级,存在着不少可能性,须要是可悲扩展的。这时候有了四种角色:

抽象工厂-是一个抽象类,不能被用于生成具体实例-他用来声明不一样产品的共性,一个系统里可能有多个抽象工厂,好比手机、平板、手提电脑等。他们统称为产品族。

具体工厂- 用来生产产品族中的某一个产品-继承自抽象工厂,实现了抽象工厂中声明的那些方法,用于建立具体产品的类。

抽象产品-抽象类,不能用于生成具体实例-上面例子中,具体产品中实现的接口会依赖一些类,这些具体产品类的共性各自抽离,对应到各自抽象产品类。

具体产品-用于生成产品族中更细粒度的产品- 好比上面的某一种操做系统,某一种硬件等。

抽象工厂就是围绕一个超级工厂来建立其余厂,JavaScript中用的不太多,上面的例子算是用ES6的方式来模拟Java的抽象类。

4、建立型3-单例模式

单例模式的核心: 整个程序有且只有一个实例。该类负责建立本身的对象,同时确保只有一个对象被建立。

单例模式比较简单,在Vuex中被应用。

有且只有一个实例,这句话让人有点困惑,通常状况下咱们建立了一个类以后,能够用new来实例化,想实例化几个就实例化几个,怎么来确保只有一个实例呢??

也就是说单例模式想实现的是无论怎么实例化,都要返回的是最初建立的那个实例。这须要该类(实质上是构造函数)有能力判断本身是否已经实例化过。

class SingDog {
    show(){
        console.log('我是一个实例对象')
    }
    // 把判断实例的逻辑放入静态方法中,其实也能够直接写入构造函数的函数体内
    static getInstance(){
        // 判断是否已经new过一个实例
        if(!SingDog.instance) {
            // 若是实例不存在,先建立他
            SingDog.instance = new SingDog()
        } else {
            // 若是已经存在实例 就直接返回
            return SingDog.instance
        }
    }
}

const instance1 = SingDog.getInstance()
const instance2 = SingDog.getInstance()
instance1 === instance2 // true复制代码

在ES6中static关键字: 

定义一个类成员,使他彻底独立于该类的任何对象
它能被类自己使用,而没必要引用任何特定的实例。

看完定义,仍是有点抽象。

类是实例的原型,以前new 一个test(),那么test中全部的方法都会被实例继承,但若是在一个方法前加上static,表示该方法不会被实例继承,而是直接经过类来调用。

父类中的静态方法能够被子类继承。

或者用闭包实现getInstance

SingDog.getInstance = (function(){
    // 定义自由变量instance, 模拟私有变量
    let instance = null
    return function(){
        // 判断instance 是否存在
        if(!instance) {
            instance = new SingDog()
        } else {
            return instance
        }
    }
})复制代码

在getInstance拦截下,无论调多少次都只有一个实例对象。

应用:Vuex

状态管理不论是redux仍是vuex,都实现全局Store存储应用的全部状态。

Vuex中使用了单一状态树,就是一个对象存储了全部状态,他是惟一一个数据源。由于用了单例模式,意味着整个应用只有一个store实例。

Vue中组件相互独立,想要通讯,通常用props,但props只用在父子之间,复杂的好比兄弟组件通讯,就用不了props了。

这时咱们能够把数据源放在全局,数据源一变,你们都变。

Vuex中保证store惟一性的办法:

Vue.use(Vuex)

new Vue({
    el: '#app',
    store
})复制代码

经过调用Vue.use()安装了一个Vuex插件,Vuex是一个对象,他内部实现了一个install方法,此方法在插件安装时被调用,将store注入到Vue实例中。每安装一次,都尝试实例一个新的store。

因此在install中就有一段判断来拦截屡次实例化。

相关文章
相关标签/搜索