GOF在《设计模式》中说到:面向接口编程,而非面向实现编程

鉴于此,这个概念可见一斑!

JS却不像其他面向对象的高级语言(C#,Java,C++等)拥有内建的接口机制,以确定一组对象和另一组对象包含相似的的特性。所幸的是JS拥有强大的灵活性,这使得模仿接口特性又变得非常简单。那么到底是接口呢?

接口概念:

接口提供了一种用以说明一个对象应该具有那些方法的手段

接口,为一些具有相似行为的类之间(可能为同一种类型,也可能为不同类型)提供统一的方法定义,使这些类之间能够很好的实现通信

使用接口的优点:

  • 自我描述性,促进代码的重用
  • 明确一个类实现的方法,帮助其使用这个类
  • 稳定不同类之间的通信

一个需求,需要多个部门协调合作的时候,接口的概念就特别重要了,每个部门可以按部就班的做自己的事情,涉及到交互的时候,提供接口处理即可

就好像主板上的内存条,CPU,硬盘,主板提供各种接口,其他设备直接按相应的接口插入即可

javascript语言要实现接口的概念还是有局限性的问题的

  • 弱类型,具有极强的表现力的语言,那么使用了接口后,其实就强化了类型的作用,降低了语言的灵活性了
  • 最重要的就是JS没有对这种机制的支持,没有强制性

javascript中模仿接口

常用的几种手法:

  • 通过注释
  • 通过属性检查模仿
  • 鸭式辨型模仿

接口本身就是一个抽象的概念,至于如何实现各有不同

这是我的一段项目代码,接口是通过模块闭包实现的,这样的好处更能体现出封装性

//引入编译模块
define('ProcessMgr', ['Compile'
], function(compile) {var flipContentProcessed,flipWidgetProcessed, disposeWidgetBind, objectKeys,converWidgetWapper, ActionMgr, getHotspot,//内部消息传递接口//
        //  1 构建节点树//       A 构建纯DOM节点//       B 构建content对象, 这是第二种加载模式,动态预加载//  2 绑定节点事件//  3 手动触发事件//  4 自动触发事件//  5 事件委托处理//  6 翻页动作//  7 翻页完成动作//  8 复位动作//  9 销毁动作//  10 移除整个页面结构//
       

ProcessMgr =

 { 'preCompile'     : preCompile,'executeCompile' : executeCompile,'assignTrigger'  : assignTrigger,'assignAutoRun'  : assignAutoRun,'assignSuspend'  : assignSuspend,'assignDispose'  : assignDispose,'recovery'       : recovery,'destroy'        : destroy,'removePage'     : removePage},...........具体处理的方法...................return ProcessMgr; //返回对外接口})

jQuery的接口更加的直接,直接挂在在对应的原型链上或是静态链


封装

为什么要封装?

因为我们不关心它是如何实现的,我们只关心如何使用

手机,电脑,我们接触的形形色色的东西,其实我们一直都只是在使用它

同样的道理,我们放到程序设计中也是如此,封装实现的细节 ,降低对象之间的耦合程序,保持数据的完整性与修改的约束

所以说一个设计良好的API可以让开发者赏心悦目

javascript实现封装的手段

对于JS语法规则,我们要牢牢抓住3点

  • JS函数是一等对象
  • JS是函数级的作用域,意味着函数内部的变量不能被外部访问
  • JS是词法性质的静态作用域,换句话说,即便在执行期作用域还是在定义的时候就预先分配好了

根据这3个规则我们就可以干很多别的语言干不了的事了

我们来模拟一个完整的封装

私有属性和方法

var encapsulation = function(){//私有属性var name = 'aaron'//私有方法var getName = function(){alert(name)}
}alert(name) //空

函数作用域实现变量与方法私有化,外边自然无法方法,当然这种完全没有实际意义了,我们要配合后面的处理

特权属性和方法

简单的说就能够让实例直接有权力访问内部的属性与方法,所以在设计上要修改下实现手法,

var encapsulation = function(){//特权this.name = 'aaron'this.getName = function(){alert(name)}
}
alert(new encapsulation().name) //aaron

弊端也显而易见,每次new一个新的对象,特权属性与方法都会被重新拷贝一份,也就是需要单独占据一块堆内存

共有属性和方法

*注意了,命名函数与函数表达式在本质上虽然没什么区别,但是在处理上还是有很大不同

函数表达式

var encapsulation = function(){//静态属性方法encapsulation.name = 'aaron'encapsulation.getName = function(){alert(name)}
}console.log( encapsulation.name ) // 无

命名函数

即便方法不执行,静态属性也能访问到,就证明了JS有一种预解析的机制,也就是常说的函数提升了

function encapsulation(){//静态属性方法encapsulation.name = 'aaron'encapsulation.getName = function(){alert(name)}
}console.log( encapsulation.name ) // aaron

共有静态属性和方法

这是最最常用的,JS 的prototype原型特性,可以共享所有属性与方法,当然,属性是引用类型的时候就会存在问题了

var encapsulation = function(){//共享属性方法encapsulation.prototype.name = 'aaron'encapsulation.prototype.getName = function(){alert(name)}
}console.log(new encapsulation().name ) // aaron
console.log(new encapsulation().name ) // aaron


继承

大型设计中,代码肯定是很多的,所以我们需要减少重复性的代码,尽可能的弱化对象之间的耦合:

通过几种手段

  • 类式继承
  • 原型式继承
  • 掺元混入

当然并非所有的重复代码都可以使用一种继承方法,所以我们一般要区分共性是否一致

我项目中使用的继承手段

呵呵,眼熟吧,借鉴EXT的处理机制,有继承与混入,自己改了点,毕竟那种分层结构,倒序调用,还有点不合适套用,下文会介绍实现

类式继承

现在的框架,库大多继承的手段都是类式继承,而且继承的处理方式也基本一致,但是里面的细节问题可能大家没注意到,我们一起分析下

我们看看几个框架的继承实现

mootools

Classvar Class = this.Class = new Type('Class', function(params){if (instanceOf(params, Function)) params = {initialize: params};var newClass = function(){reset(this);if (newClass.$prototyping) return this;this.$caller = null;var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;this.$caller = this.caller = null;return value;}.extend(this).implement(params);newClass.$constructor = Class;newClass.prototype.$constructor = newClass;newClass.prototype.parent = parent;return newClass;
});var parent = function(){if (!this.$caller) throw new Error('The method "parent" cannot be called.');var name = this.$caller.$name,parent = this.$caller.$owner.parent,previous = (parent) ? parent.prototype[name] : null;if (!previous) throw new Error('The method "' + name + '" has no parent.');return previous.apply(this, arguments);
};

Backbone

extend    // Shared empty constructor function to aid in prototype-chain creation.var ctor = function () {};// Helper function to correctly set up the prototype chain, for subclasses.// Similar to `goog.inherits`, but uses a hash of prototype properties and// class properties to be extended.var extend = function (protoProps, staticProps) {var parent = this;var child;// The constructor function for the new subclass is either defined by you// (the "constructor" property in your `extend` definition), or defaulted// by us to simply call the parent's constructor.if (protoProps && protoProps.hasOwnProperty('constructor')) {child = protoProps.constructor;} else {child = function () {parent.apply(this, arguments);};}// Inherit class (static) properties from parent._.extend(child, parent);// Set the prototype chain to inherit from `parent`, without calling// `parent`'s constructor function.
        ctor.prototype = parent.prototype;child.prototype = new ctor();// Add prototype properties (instance properties) to the subclass,// if supplied.if (protoProps) _.extend(child.prototype, protoProps);// Add static properties to the constructor function, if supplied.if (staticProps) _.extend(child, staticProps);//执行完child.prototype=new ctor后,child.prototype.constructor已经不指向child,所以此处需要显示设置child.prototype.constructor = child;// Set a convenience property in case the parent's prototype is needed later.child.__super__ = parent.prototype;return child;};

ext

extend   /****  继承父类或者base顶层基类**/Class.registerPreprocessor('extend', function(cls, data, fn) {var extend = data.extend,base = Xut.Base,temp = function() {},parent, i, k, ln, staticName, parentStatics;delete data.extend;//继承顶级base,默认继承父类if (typeof extend === 'function' && extend !== Object) {parent = extend;} else {parent = base;}temp.prototype = parent.prototype;cls.prototype = new temp();if (!('$class' in parent)) {for (i in base.prototype) {if (!parent.prototype[i]) {parent.prototype[i] = base.prototype[i];}}}cls.prototype.self = cls;if (data.hasOwnProperty('constructor')) {cls.prototype.constructor = cls;} else {cls.prototype.constructor = parent.prototype.constructor;}cls.superclass = cls.prototype.superclass = parent.prototype;delete data.extend;fn.call(this, cls, data);});

ext比较特殊,因为是通过注入的手法,实现继承了类名转化继承混入静态扩充

对比几个框架,我们红线部分的相同之处没?实现原理确是一样的,包括EXT也一样,只是在具体实现上增加了各自的方案,


设计的原理

抛开复杂的框架,我们看看设计的底层原理是什么,又会有什么问题?

原型链作为JS实现继承的主要方法,其根本的思路利用原型链让一个引用类型,继承另一个引用类型

原型与实例的关系:

构造器有一个原型对象,原型对象有一个指针指向构造器,那么实例则是包含一个指向原型的指针

所以实例只与原型有关系

 
 
实现中的细节:
function SuperType(){ //父类this.property = true;
} SuperType.prototype.getSuperValue = function(){return this.property;
}; function SubType(){ //子类this.subproperty = false;
} //继承了 SuperType
SubType.prototype = new SuperType(); //实现原型继承,引用 SubType.prototype.getSubValue = function (){ return this.subproperty;
}; var instance = new SubType(); alert(instance.getSuperValue());      //true

 
 
 
1 继承的本质是引用,那么N多组实例其实都是操作的同一个引用,那么问题来了,如果父类中有个一引用属性,那么一个子类操作修改了,所有的子类都会被影响
 
function SuperType(){ this.colors = ["red", "blue", "green"];
}function SubType(){
} //继承了 SuperType
SubType.prototype = new SuperType(); var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);     //"red,blue,green,black" var instance2 = new SubType();
alert(instance2.colors);       //"red,blue,green,black"

 
 
2 在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术(有时候也叫做伪造对象或经典继承)
 
function SuperType(){ this.colors = ["red", "blue", "green"];
} function SubType(){   //继承了 SuperType SuperType.call(this);
} var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);    //"red,blue,green,black" var instance2 = new SubType();
alert(instance2.colors);    //"red,blue,green"

通过使用 call()方法(或 apply()方法也可以),我们实际上是在(未来将要)新创建的 SubType 实例的环境下调用了 SuperType 构造函数。这样一来,就会在新 SubType 对象上执行 SuperType()函数中定义的所有对象初始化代码。结果,SubType 的每个实例就都会具有自己的 colors 属性的副本了

借用构造函数的问题
如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的

通过SubType.prototype = new SuperType(); 的方式实现引用的继承,看似很简单,但是里面还有几个问题

  • 不管什么情况,都把父类的实例属性与方法都复制给了子类
  • 如果子类修改了原型共享的引用属性,倒置所有继承的会受引向
  • 借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定义,因此函数复用就无从谈起了

看看Backbone的如何处理

var ctor = function () {};

ctor.prototype = parent.prototype;child.prototype = new ctor();child.prototype.constructor = child;

可见backbone引用一个中介ctor函数

其实就是一种代理继承

proxy来实现继承,这样就避免了在classical继承中出现的parent的constructor被使用两次带来的效率问题和在原型链中再次继承this的属性
function obj (o){var f = {}f.prototype = oreturn new f();
}

最后还要修正一下constructor的指针,因为是继承ctor了

至于原型式继承可以参考高级程序设计3, 比较详细了

Jser 设计模式系列之面向对象 - 接口封装与继承相关推荐

  1. python面向对象编程(封装与继承)

    一. 面向过程编程语言 "面向过程"(Procedure Oriented)是一种以过程为中心的编程思想.分析出解决问题所需要的步 骤,然后用函数把这些步骤一步一步实现,使用的时候 ...

  2. 如何理解面向对象的封装、继承、多态

    如何理解面向对象的封装.继承.多态 面向对象可以说是一种对现实是事物的抽象,将一类事物抽象成一个类,类里面包含了这类事物具有的公共部分,以及我们对这些部分的操作,也就是对应的数据和过程. 面向对象思想 ...

  3. W6_面向对象_封装_继承_多继承_多态

    W6_面向对象_封装_继承_多继承_多态 80.81.82.83.第02章节-Python3.5-面向对象介绍 84.第05章节-Python3.5-实例变量与类变量 85.第06章节-Python3 ...

  4. python面向对象编程 -- 封装、继承

    面向对象编程 -- 封装.继承 面向对象编程三要素:封装.继承和多态.本文主要看和封装.继承相关的概念:在python中多态的概念比较模糊,本文不做讨论. 1 封装 封装:将数据和操作组装到一起,对外 ...

  5. Java SE(六)之面向对象(封装,继承,多态,接口)

    文章目录 类和对象 1. 创建一个类 2. 创建一个对象 3. 访问变量和方法 4. some tips 封装 修饰符 1. 访问控制修饰符 2. 非访问修饰符 (1)static (2)final ...

  6. Go 学习笔记(36)— 基于Go方法的面向对象(封装、继承、多态)

    Go 面向对象编程的三大特性:封装.继承和多态. 封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式 继承:使得子类具有父类的属性和方法或者重新定义.追加属性和方法等 多态:不同对象中同种行为的不 ...

  7. 3、C#面向对象:封装、继承、多态、String、集合、文件(下)

    面向对象多态 一.装箱和拆箱 装箱:将值类型转换为引用类型.object o = 1:值类型给引用类型赋值 拆箱:将引用类型转换为值类型.int n = (int)o; 强制转换为值类型 满足条件:两 ...

  8. extend implements多个对象_「每天三分钟跟我学Java」之Java面向对象的封装、继承、多态...

    Java是面向对象的语言,深入理解面向对象的概念,对Java的开发至关重要.本节我们着重看下面向对象的三大特性,封装.继承.多态. 一.封装 封装是将类的某些信息隐藏在类内部,不允许外部程序直接访问, ...

  9. java程序员从笨鸟到菜鸟之_Java程序员从笨鸟到菜鸟之(二)面向对象之封装,继承,多态(上)...

    Java是一种面向对象的语言,这是大家都知道的,他与那些像c语言等面向过程语言不同的是它本身所具有的面向对象的特性--封装,继承,多态,这也就是传说中的面向对象三大特性 一:从类和对象开始说起: Oo ...

最新文章

  1. 1055 The World‘s Richest
  2. python 类变量、实例变量、参数、实例方法、类方法、静态方法 的用法和区别
  3. 动态为程序指定快捷键
  4. tensorflow教程 开始——数据集:快速了解 tf.data
  5. ORACLE RAC 中 SRVCTL 命令详细说明
  6. 计算机体系结构 -- 第一章3 -- 设计的定量4个原则
  7. 域名是如何被墙的_如何快速搭建属于自己的个性网站?
  8. php 打乱数组顺序_PHP实现大转盘抽奖算法
  9. ROS调用ORB-SLAM2
  10. Windows系统安装adb/fastboot驱动教程
  11. ubuntu20.04.1下安装qt4相关依赖库
  12. 浙江概况——经济发展篇
  13. java网络文章博客抓取系统_java 后端博客系统文章系统——No5
  14. 如何在苹果iPhone或iPad上启用SSL证书
  15. 【kafka异常】使用Spring-kafka遇到的坑
  16. vue实现音乐平台项目
  17. matlab 区域生长算法生成二值图像边界区域
  18. 中国计算机技术职业资格网(软考)考试用书(大纲、教程、辅导用书)
  19. json解析格式化工具
  20. Java实现拼图小游戏(7)—— 计步功能及菜单业务的实现

热门文章

  1. sublime 安装 Package Control(笔记)
  2. vue动画transition(笔记)
  3. 酷炫好看的横向滑动个人介绍简历模板
  4. YzmCMS轻量级开源CMS v6.2
  5. Golang开发的跨平台蜜罐平台HFish v0.6.4源码
  6. 在Nginx上配置NameCheap免费SSL
  7. WebBrowser中显示乱码
  8. C# 代码生成器 amp; 网站架构设计
  9. 通过IHttpHandlerFactory,过滤TextBox、Input和Textarea中的特殊字符
  10. Magento教程 25:如何修改系统发送的通知信件?