用JS来理解设计模式(一)

前言

最近在看Head First设计模式,想好好学一下设计模式相关的知识。这本书要求看书的人须要会java和OO(即面向对象)设计原则,好在二者在学校都学过。可是平时在写js和业务代码的时候不多用到相关的知识,而咱们经常使用到的库和框架,却无处不存在着这些知识,因此想本身写一些库或者框架的人,学会设计模式仍是颇有必要的。我以为设计模式和算法有一些类似之处,它们都是一种思想,是一种解决方法。设计模式不是具体的代码和具体的实现,而是和算法同样,是以一种思想存在,当咱们想实现某种功能,或者构思框架和库的时候,相关的解决方案便浮如今你的脑海里,因此说设计模式不是说就必定是一种模式,有多是模式的组合,模式基础上的变化。固然,以上所说也只是我看了前几章的想法。最主要的仍是书中所写的实现代码皆是java,因此仍是想着用js写一遍,毕竟js看起来不太像是拿来写面向对象的(不过es6已经有Class了)。因此写这个也权当作是自我记录吧。java

策略模式

策略模式定义了算法族,分别封装起来,让它们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。

看了这个正式定义后多是很差理解的,不过不要紧,讲个书里的例子就明白了。es6

小李的公司要求他开发一套模拟鸭子的游戏,所以他写了个超类(即父类)Duck,并让各类鸭子继承此超类。(如下代码皆用js来编写)算法

class Duck {
    quack () {} 
    swim () {}
    display () {}
}

他给鸭子超类定义了鸭子叫、游泳、展现方法。可是因为每一种鸭子的外观不一样,所以在这里的定义是抽象方法(即在超类中不实现,交由子类负责实现,但在js中并无这种定义),而后在每一个鸭子子类中进行实现本身的display方法。编程

就这样,小李完成了一个简单的模拟鸭子游戏。可是,次日,产品经理让小李给鸭子加一个飞行的方法。小李心想这还不简单吗,在Duck超类里加一个fly () {}方法就完事了,这就是面向对象的威力。设计模式

然而,在次日的演示中,产品经理惊讶的发现‘橡皮鸭子’也能在屏幕飞来飞去。小李心想糟糕了,因而赶忙在橡皮鸭的类中进行了fly方法覆盖,虽然暂时解决了问题,可是一想到往后还要添加各类各样的鸭子,每只️鸭子的飞行方法和叫声都不同,难道每次添加新类的时候都要去覆盖和检查吗?框架

设计原则: 找出应用中可能须要变化的地方,而后将它们独立出来,不要与那些不须要变化的代码混在一块儿。

根据这个原则来分析鸭子的行为,就目前来看,鸭子叫和飞行方法是会随着鸭子的不一样而改变的,所以须要将两个方法取出来,创建新的类来表明行为。那为何不取出display呢?这件事书里并无提到,可是稍稍分析便知道了,display对于每一个鸭子子类来讲都是一定须要设置的,又不存在相同的状况,所以没有潜在的复用可能性,就是独立成display类也仍是依附于特定鸭子子类的,所以没有必要将展现方法取出来。this

设计原则: 针对接口编程,而不是针对实现编程。

这里的接口指的是面向对象里的一个关键字interface,接口就像是一个定义好了全部方法的类,可是其中的每个方法都不进行实际代码的实现,而是交由继承它的子类来实现方法。它是一种约束,继承了它的子类都必须实现。所以,当咱们在使用接口的时候,咱们能确定的去使用继承了某个接口的类的方法,即就是方法必然存在。所以在编写的时候无需关心具体的代码实现(以上是我的的理解)。设计

所以书中分别建了FlyBehaviorQuackBehavior这两个接口,不过js中并无接口这种定义,可是js中并无与接口功能相似的方法存在,只能写个类去代替它,可是这个类并无约束效力,所以从这里也能够看出来TS的诞生缘由。code

class FlyBehavior {
    fly () {
        console.log('未定义飞行方法');
    }
}

class FlyWithWings extends FlyBehavior {
    fly () {
        console.log('FlyWithWings');
    }
}

class FlyNoWay extends FlyBehavior {
    fly () {
        console.log('FlyNoWay');
    }
}

QuackBehavior的实现与上面相似。orm

这样的设计,可让飞行和呱呱叫的动做被其余对象复用,由于这些行为已经与鸭子类无关了。而咱们能够新增一些行为,不会影响到既有的行为类,也不会影响使用到飞行行为的鸭子类。

此时再来重写编写鸭子类,就变成了以下。

class Duck {
    flyBehavior
    quackBehavior
    swim () {}
    display () {}
    setFly (fly) {
        this.flyBehavior = fly;
    }
    setQuack (quack) {
        this.quackBehavior = quack;
    }
    performFly () {
        this.flyBehavior.fly();
    }
    performQuack () {
        this.quackBehavir.quack();
    }
}

能够看到此时咱们再也不在意具体的fly方法或者quack 方法是怎么样的,咱们只须要去调用方法便可,同时还能够在运行时进行方法的替换,经过调用setQuacksetFly方法。

此时来写一个具体的鸭子类。

class MallardDuck extends Duck {
    constructor () {
        super();
        this.setQuack(new Quack());
        this.setFly(new FlyWithWings());
    }
    display () {} // 记得重写
}

到这里大概差很少大概就可以理解策略模式是怎么样的了,有时候咱们能够把一组行为认为是一族算法,而后封装起来,互相独立,能够随意替换,这差很少就是策略模式的面貌了。

设计原则:多用组合,少用继承。
相关文章
相关标签/搜索