JS设计模式-前言
JS设计模式这一系列笔记(搬运)出自《Javascript设计模式与开发实践》一书。究竟为什么要用设计模式,以及它们在js程序中的运用,如何使代码更高校、更容易处理。
设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。
在软件设计中,模式是一些经过了大量实际项目验在软件设计中,模式是一些经过了大量实际项目验证的优秀解决方案。熟悉这些模式的程序员,对某些模式的理解也许形成了条件反射。当合适的场景出现时,他们可以很快地找到某种模式作为解决方案。
模式在不同语言之间的区别
《设计模式》一书的副标题是“可复用面向对象软件的基础”。《设计模式》这本书完全是从面向对象设计的角度出发的,通过对封装、继承、多态、组合等技术的反复使用,提炼出一些可重复使用的面向对象设计技巧。
设计模式实际上是解决某些问题的一种思想,与具体使用的语言无关。模式社区和语言一直都在发展,如今,除了主流的面向对象语言,函数式语言的发展也非常迅猛。在函数式或者其他编程范型的语言中,设计模式依然存在。但因为语言的不同,一些设计模式在另外一些语言中的实现也许跟《设计模式》一书中的大相径庭。
比如,Command模式在Java中需要一个命令类,一个接收者类,一个调用者类。Command模式把运算块封装在命令对象的方法内,成为该对象的行为,并把命令对象四处传递。但在JavaScript这些把函数当作一等对象的语言中,函数便能封装运算块,并且函数可以被当成对象一样四处传递,这样一来,命令模式在JavaScript中就成为了一种隐形的模式。
在Java这种静态编译型语言中,无法动态地给已存在的对象添加职责,所以一般通过包装类的方式来实现装饰者模式。但在JavaScript这种动态解释型语言中,给对象动态添加职责是再简单不过的事情。这就造成了JavaScript语言的装饰者模式不再关注于给对象动态添加职责,而是关注于给函数动态添加职责。
设计模式的适用性
从某些角度来看,设计模式确实有可能带来代码量的增加,或许也会把系统的逻辑搞得更复杂。但软件开发的成本并非全部在开发阶段,设计模式的作用是让人们写出可复用和可维护性高的程序。
假设有一个空房间,我们要日复一日地往里面放一些东西。最简单的办法当然是把这些东西直接扔进去,但是时间久了,就会发现很难从这个房子里找到自己想要的东西,要调整某几样东西的位置也不容易。所以在房间里做一些柜子也许是个更好的选择,虽然柜子会增加我们的成本,但它可以在维护阶段为我们带来好处。使用这些柜子存放东西的规则,或许就是一种模式。
所有设计模式的实现都遵循一条原则,即“找出程序中变化的地方,并将变化封装起来”。一个程序的设计总是可以分为可变的部分和不变的部分。当我们找出可变的部分,并且把这些部分封装起来,那么剩下的就是不变和稳定的部分。这些不变和稳定的部分是非常容易复用的。这也是设计模式为什么描写的是可复用面向对象软件基础的原因。
模式应该用在正确的地方。而哪些才算正确的地方,只有在我们深刻理解了模式的意图之后,再结合项目的实际场景才会知道。
分辨模式的关键是意图而不是结构
在设计模式的学习中,有人经常发出这样的疑问:代理模式和装饰者模式,策略模式和状态模式,策略模式和智能命令模式,这些模式的类图看起来几乎一模一样,它们到底有什么区别?
实际上这种情况是普遍存在的,许多模式的类图看起来都差不多,模式只有放在具体的环境下才有意义。比如我们的手机,把它当电话的时候,它就是电话;把它当闹钟的时候,它就是闹钟;用它玩游戏的时候,它就是游戏机。有很多模式的类图和结构确实很相似,但这不太重要,辨别模式的关键是这个模式出现的场景,以及为我们解决了什么问题。
对JavaScript设计模式的误解
第一个问题是习惯把静态类型语言的设计模式照搬到JavaScript中,比如为了模拟JavaScript版本的工厂方法(FactoryMethod)模式,而生硬地把创建对象的步骤延迟到子类中。实际上,在Java等静态类型语言中,让子类来“决定”创建何种对象的原因是为了让程序迎合依赖倒置原则(DIP)。在这些语言中创建对象时,先解开对象类型之间的耦合关系非常重要,这样才有机会在将来让对象表现出多态性。
而在JavaScript这种类型模糊的语言中,对象多态性是天生的,一个变量既可以指向一个类,又可以随时指向另外一个类。JavaScript不存在类型耦合的问题,自然也没有必要刻意去把对象“延迟”到子类创建,也就是说,JavaScript实际上是不需要工厂方法模式的。模式的存在首先是能为我们解决什么问题,这种牵强的模拟只会让人觉得设计模式既难懂又没什么用处。
另一个问题是习惯根据模式的名字去臆测该模式的一切。比如命令模式本意是把请求封装到对象中,利用命令模式可以解开请求发送者和请求接受者之间的耦合关系。但命令模式经常被人误解为只是一个名为execute的普通方法调用。这个方法除了叫作execute之外,其实并没有看出其他用处。所以许多人会误会命令模式的意图,以为它其实没什么用处,从而联想到其他设计模式也没有用处。这些误解都影响了设计模式在JavaScript语言中的发展。