设计模式的定义:在面向对象软件设计过程当中针对特定问题的简洁而优雅的解决方案。javascript
人类能够走到生物链顶端的前两个缘由分别是会“使用名字”和“使用工具”。html
全部设计模式都遵循一条原则,即“找出程序中变化的地方,并将变化封装起来”java
API:(应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或某硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工做机制的细节。编程
绝大部分设计模式的实现都离不开多态性的思想,因此在学习设计模式以前先来了解一下多态小程序
一、静态语言中的多态:设计模式
//多态 //这种方法没有消除类型之间的耦合关系 /* 耦合:就是代码关系过于紧密,每每改一小段代码,须要整个项目作很大的改动。 * 在实际开发中应该尽可能避免太高的耦合。接口、继承是解耦合的一种好方法。 */ class Duck { public void makeSound() { System.out.println("嘎嘎嘎"); } } class Chicken { public void makeSound() { System.out.println("咯咯咯"); } } class AnimalSound { public void makeSound(Duck duck) { duck.makeSound(); } // public void makeSound(Chicken chicken) { // chicken.makeSound(); // } } public class Test { public static void main(String[] args) { AnimalSound animalSound = new AnimalSound(); Duck duck = new Duck(); animalSound.makeSound(duck); // Chicken chicken = new Chicken(); // animalSound.makeSound(chicken); } }
二、静态语言中,继承实现多态效果:浏览器
// 继承实现多态效果 /* 多态的思想其实是把“作什么”和“谁去作”分离开来,要实现这一点,归根结底先要消除类型之间的耦合关系。 * 若是类型之间的耦合关系没有被消除,那么咱们在makeSound方法中指定了发出叫声的对象是某个类型,它就不可能再被替换成另一个类型 * 在java中,能够经过向上转型来实现多态*/ abstract class Animal { abstract void makeSound(); //抽象方法 } class Duck extends Animal{ public void makeSound() { System.out.println("嘎嘎嘎"); } } class Chicken extends Animal{ public void makeSound() { System.out.println("咯咯咯"); } } class AnimalSound { public void makeSound(Animal animal) { //接受Animal类型的参数 animal.makeSound(); } } public class Test { public static void main(String[] args) { AnimalSound animalSound = new AnimalSound(); Duck duck = new Duck(); animalSound.makeSound(duck); Chicken chicken = new Chicken(); animalSound.makeSound(chicken); } }
三、JavaScript中的多态:函数
/*JavaScript的多态性是与生俱来的 JavaScript做为一门动态类型语言,它在编译时没有类型(包括对象类型和参数类型)检查的过程, 即不存在任何程度上的“类型耦合”,不须要向上转型*/ var googleMap = { show: function() { console.log('开始渲染谷歌地图'); } }; var baiduMap = { show: function() { console.log('开始渲染百度地图'); } }; // var sosoMap = { // show: function() { // console.log('开始渲染搜搜地图'); // } // }; var renderMap = function(type) { if (type=='googleMap') { googleMap.show(); }else if (type=='baiduMap') { baiduMap.show(); } // else if (type=='sosoMap') { // sosoMap.show(); // } }; renderMap('googleMap'); renderMap('baiduMap'); // renderMap('sosoMap');
var googleMap = { show: function() { console.log('开始渲染谷歌地图'); } }; var baiduMap = { show: function() { console.log('开始渲染百度地图'); } }; // var sosoMap = { // show: function() { // console.log('开始渲染搜搜地图'); // } // }; var renderMap = function(type) { if (type.show instanceof Function) { //Function必须大写,缘由能够参考 //连接1:https://www.cnblogs.com/shuiyi/p/5343399.html //连接2:http://www.cnblogs.com/shuiyi/p/5305435.html //连接3:https://zhidao.baidu.com/question/134033299.html?qbl=relate_question_5&word=function%20%BA%CDfunction type.show(); } }; renderMap(googleMap); renderMap(baiduMap); // renderMap(sosoMap);
原型模式和基于原型继承的 JavaScript对象系统 工具
一、使用克隆的原型模式学习
从设计模式的角度讲,原型模式是用于建立对象的一种模式,若是咱们想要建立一个对象, 一种方法是先指定它的类型,而后经过类来建立这个对象。原型模式选择了另一种方式,咱们 再也不关心对象的具体类型,而是找到一个对象,而后经过克隆来建立一个如出一辙的对象。
原型模式的实现关键,是语言自己是否提供了clone方法。ECMAScript 5提供了Object.create 方法,能够用来克隆对象。代码以下:
var Plane = function() { this.blood = 100; this.attackLevel = 1; this.defenseLevel = 1; }; var plane = new Plane(); plane.blood = 500; plane.attackLevel = 10; plane.defenseLevel = 7; var clonePlane = Object.create(plane); //Object.create()方法能够用来克隆对象 // console.log(Plane); console.log(clonePlane); // 在不支持Object.create()的浏览器中,则可使用如下代码: Object.create = Object.create || function(obj) { var F = function() {}; F.prototype = obj; return new F(); }
固然在 JavaScript这种类型模糊的语言中,建立对象很是容易,也不存在类型耦合的问题。 从设计模式的角度来说,原型模式的意义并不算大 。但 JavaScript自己是一门基于原型的面向对 象语言,它的对象系统就是使用原型模式来搭建的,在这里称之为原型编程范型也许更合适。
二、体验 Io语言
事实上,使用原型模式来构造面向对象系统的语言远非仅有 JavaScript一家。 JavaScript 基于原型的面向对象系统参考了 Self 语言和 Smalltalk 语言,为了搞清 JavaScript 中的原型,咱们本该寻根溯源去瞧瞧这两门语言。但因为这两门语言距离如今实在太遥远,咱们 不妨转而了解一下另一种轻巧又基于原型的语言——Io语言。 Io语言在 2002年由 Steve Dekorte发明。能够从http://iolanguage.com下载到 Io语言的解释器, 安装好以后打开 Io解释器,输入经典的“Hello World”程序。解释器打印出了 Hello World的字符串,这说明咱们已经可使用 Io语言来编写一些小程序了,以下图所示。
三、原型编程范型的一些规则
跟使用“类” 的语言不同的地方是,Io语言中初只有一个根对象 Object,其余全部的对象都克隆自另一 个对象。若是 A对象是从 B对象克隆而来的,那么 B对象就是 A对象的原型。
在上一小节的例子中,Object 是 Animal 的原型,而 Animal 是 Dog 的原型,它们之间造成了一 条原型链。这个原型链是颇有用处的,当咱们尝试调用 Dog 对象的某个方法时,而它自己却没有 这个方法,那么 Dog 对象会把这个请求委托给它的原型 Animal 对象,若是 Animal 对象也没有这个属性,那么请求会顺着原型链继续被委托给 Animal 对象的原型 Object 对象,这样一来便能获得继承的效果,看起来就像 Animal 是 Dog 的“父类”,Object 是 Animal 的“父类”。 这个机制并不复杂,却很是强大,Io和 JavaScript同样,基于原型链的委托机制就是原型继承的本质。
咱们来进行一些测试。在 Io的解释器中执行 Dog makeSound 时,Dog 对象并无 makeSound 方 法,因而把请求委托给了它的原型 Animal 对象 ,而 Animal 对象是有 makeSound 方法的,因此该条 语句能够顺利获得输出,如图 1-2所示。
咱们能够发现原型编程范型至少包括如下基本规则。
全部的数据都是对象。
要获得一个对象,不是经过实例化类,而是找到一个对象做为原型并克隆它。
对象会记住它的原型。
若是对象没法响应某个请求,它会把这个请求委托给它本身的原型。
四、JavaScript中的原型继承
咱们不能说在 JavaScript中全部的数据都是对象,但能够说绝大部分数据都是对象。那么相 信在 JavaScript中也必定会有一个根对象存在,这些对象追根溯源都来源于这个根对象。 事实上,JavaScript中的根对象是 Object.prototype 对象。Object.prototype 对象是一个空的对象。咱们在 JavaScript 遇到的每一个对象,实际上都是从 Object.prototype 对象克隆而来的, Object.prototype 对象就是它们的原型。好比下面的 obj1 对象和 obj2 对象:
var obj1 = new Object(); var obj2 = {};
能够利用 ECMAScript 5提供的 Object.getPrototypeOf 来查看这两个对象的原型:
console.log( Object.getPrototypeOf( obj1 ) === Object.prototype ); // 输出:true
console.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 输出:true
五、准确的来讲:若是对象没法响应某个请求,它会把这个请求委托给它的构造器的原型
实际上,虽然 JavaScript 的对象最初都是由 Object.prototype 对象克隆而来的,但对象构造 器的原型并不只限于 Object.prototype 上,而是能够动态指向其余对象。这样一来,当对象 a 需 要借用对象 b 的能力时,能够有选择性地把对象 a 的构造器的原型指向对象 b,从而达到继承的 效果。下面的代码是咱们最经常使用的原型继承方式:
var obj = { name: 'sven' }; var A = function(){}; A.prototype = obj;
一、首先,尝试遍历对象 a 中的全部属性,但没有找到 name 这个属性。 二、查找 name 属性的这个请求被委托给对象 a 的构造器的原型,它被 a.__proto__ 记录着而且 指向 A.prototype,而 A.prototype 被设置为对象 obj。 三、在对象 obj 中找到了 name 属性,并返回它的值。
当咱们指望获得一个“类”继承自另一个“类”的效果时,每每会用下面的代码来模拟实现:
var A = function(){}; A.prototype = { name: 'sven' }; var B = function(){}; B.prototype = new A(); varb=newB(); console.log( b.name ); // 输出:sven
再看这段代码执行的时候,引擎作了什么事情。
一、首先,尝试遍历对象 b 中的全部属性,但没有找到 name 这个属性。 二、查找 name 属性的请求被委托给对象 b 的构造器的原型,它被 b.__proto__ 记录着而且指向 B.prototype,而 B.prototype 被设置为一个经过 new A()建立出来的对象。 三、在该对象中依然没有找到 name 属性,因而请求被继续委托给这个对象构造器的原型 A.prototype。 四、在 A.prototype 中找到了 name 属性,并返回它的值。
最后还要留意一点,原型链并非无限长的。如今咱们尝试访问对象 a 的 address 属性。而 对象 b 和它构造器的原型上都没有 address 属性,那么这个请求会被最终传递到哪里呢?
实际上,当请求达到 A.prototype,而且在 A.prototype 中也没有找到 address 属性的时候, 请求会被传递给 A.prototype 的构造器原型 Object.prototype,显然 Object.prototype 中也没有 address 属性,但 Object.prototype 的原型是 null,说明这时候原型链的后面已经没有别的节点了。 因此该次请求就到此打住,a.address 返回 undefined。
a.address // 输出:undefined