JavaScript设计模式开发中组合模式的使用教程


Posted in Javascript onMay 18, 2016

我们平时开发过程中,一定会遇到这种情况:同时处理简单对象和由简单对象组成的复杂对象,这些简单对象和复杂对象会组合成树形结构,在客户端对其处理的时候要保持一致性。比如电商网站中的产品订单,每一张产品订单可能有多个子订单组合,比如操作系统的文件夹,每个文件夹有多个子文件夹或文件,我们作为用户对其进行复制,删除等操作时,不管是文件夹还是文件,对我们操作者来说是一样的。在这种场景下,就非常适合使用组合模式来实现。

基本知识

组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式主要有三个角色:
(1)抽象组件(Component):抽象类,主要定义了参与组合的对象的公共接口
(2)子对象(Leaf):组成组合对象的最基本对象
(3)组合对象(Composite):由子对象组合起来的复杂对象
理解组合模式的关键是要理解组合模式对单个对象和组合对象使用的一致性,我们接下来说说组合模式的实现加深理解。
组合模式算是为在页面动态创建UI量身定做的,你可以只使用一条命=命令为许多对象初始化一些复杂的或者递归的操作.组合模式提供了两个有点:
(1)允许你将一组对象当成特定的对象.组合对象(A composite)和组成它的子对象实现相同的操作.对组合对象执行某一个操作将会使该对象下的所有子对象执行相同的操作.因此你不仅可以无缝的替换单个对象为一组对象集合,反过来也一样.这些独立的对象之间即所谓松散耦合的.
(2)组合模式会将子对象集组合成树结构并且允许遍历整个树.这样可以隐藏内部实现并且允许你以任意的方式组织子对象.这个对象(组合对象)的任何代码将不会依赖内部子对象的实现.

组合模式的实现

(1)最简单的组合模式

HTML文档的DOM结构就是天生的树形结构,最基本的元素醉成DOM树,最终形成DOM文档,非常适用适用组合模式。
我们常用的jQuery类库,其中组合模式的应用更是频繁,例如经常有下列代码实现:

$(".test").addClass("noTest").remove("test");

这句简单的代码就是获取class包含test的元素,然后进行addClass和removeClass处理,其中不论$(“.test”)是一个元素,还是多个元素,最终都是通过统一的addClass和removeClass接口进行调用。
我们简单模拟一下addClass的实现:

var addClass = function (eles, className) {
  if (eles instanceof NodeList) {
    for (var i = 0, length = eles.length; i < length; i++) {
      eles[i].nodeType === 1 && (eles[i].className += (' ' + className + ' '));
    }
  }
  else if (eles instanceof Node) {
    eles.nodeType === 1 && (eles.className += (' ' + className + ' '));
  }
  else {
    throw "eles is not a html node";
  }
}
addClass(document.getElementById("div3"), "test");
addClass(document.querySelectorAll(".div"), "test");

这段代码简单的模拟了addClass的实现(暂不考虑兼容性和通用性),很简单地先判断节点类型,然后根据不同类型添加className。对于NodeList或者是Node来说,客户端调用都是同样的使用了addClass这个接口,这个就是组合模式的最基本的思想,使部分和整体的使用具有一致性。

(2)典型的例子

前面我们提到一个典型的例子:产品订单包含多个产品子订单,多个产品子订单组成一个复杂的产品订单。由于Javascript语言的特性,我们将组合模式的三个角色简化成2个角色:
(1)子对象:在这个例子中,子对象就是产品子订单
(2)组合对象:这里就是产品的总订单
假设我们开发一个旅游产品网站,其中包含机票和酒店两种子产品,我们定义了子对象如下:

function FlightOrder() { }
FlightOrder.prototyp.create = function () {
  console.log("flight order created");
}
function HotelOrder() { }
HotelOrder.prototype.create = function () {
  console.log("hotel order created");
}

上面的代码定义了两个类:机票订单类和酒店订单类,每个类都有各自的订单创建方法。
接下来我们创建一个总订单类:

function TotalOrders() {
  this.orderList = [];
}
TotalOrders.prototype.addOrder = function (order) {
  this.orderList.push(order);
}
TotalOrders.prototype.create = function (order) {
  for (var i = 0, length = this.orderList.length; i < length; i++) {
    this.orderList[i].create();
  }
}

这个对象主要有3个成员:订单列表,添加订单的方法,创建订单的方法。
在客户端使用的时候如下:

var flight = new FlightOrder();
flight.create();

var orders = new TotalOrders();
orders.addOrder(new FlightOrder());
orders.addOrder(new HotelOrder());
orders.create();

客户端调用展示了两种方式,一种是单一的创建机票订单,一种是创建多张订单,但最终都是通过create方法进行创建,这就是一个很典型的组合模式的应用场景。

总结
组合模式并不难理解,它主要解决的是单一对象和组合对象在使用方式上的一致性问题。如果对象具有明显的层次结构并且想要统一地使用它们,这就非常适合使用组合模式。在Web开发中,这种层次结构非常常见,很适合使用组合模式,尤其是对于JS来说,不用拘泥于传统面向对象语言的形式,灵活地利用JS语言的特性,达到对部分和整体使用的一致性。
(1)使用组合模式的场景
在遇到下面两种情况的时候才使用组合模式
A.含有某种层级结构的对象集合(具体结构在开发过程中无法确定)
B.希望对这些对象或者其中的某些对象执行某种操作
(2)组合模式的缺点
因为组合对象的任何操作都会对所有的子对象调用同样的操作,所以当组合的结构很大时会有性能问题。还有就是使用组合模式封装HTML时要选择合适的标签,比如table就不能用于组合模式,叶子节点不明显

Javascript 相关文章推荐
jQuery 位置插件
Dec 25 Javascript
js form 验证函数 当前比较流行的错误提示
Jun 23 Javascript
javascript中的new使用
Mar 20 Javascript
JQuery扩展插件Validate 5添加自定义验证方法
Sep 05 Javascript
jquery获取div距离窗口和父级dv的距离示例
Oct 10 Javascript
使用js实现按钮控制文本框加1减1应用于小时+分钟
Dec 09 Javascript
构造函数+原型模式构造js自定义对象(最通用)
May 12 Javascript
针对后台列表table拖拽比较实用的jquery拖动排序
Oct 10 Javascript
Node.js的特点详解
Feb 03 Javascript
浅谈在node.js进入文件目录的问题
May 13 Javascript
解决webpack dev-server不能匹配post请求的问题
Aug 24 Javascript
Vue入门学习笔记【基本概念、对象、过滤器、指令等】
Apr 13 Javascript
设计模式中的组合模式在JavaScript程序构建中的使用
May 18 #Javascript
easyui window refresh 刷新两次的解决方法(推荐)
May 18 #Javascript
详解JavaScript设计模式开发中的桥接模式使用
May 18 #Javascript
jquery解析XML及获取XML节点名称的实现代码
May 18 #Javascript
Jquery跨域获得Json的简单实例
May 18 #Javascript
jQuery 获取跨域XML(RSS)数据的相关总结分析
May 18 #Javascript
jQuery使用ajax跨域获取数据的简单实例
May 18 #Javascript
You might like
PHP基于SMTP协议实现邮件发送实例代码
2017/04/27 PHP
PHP面向对象之事务脚本模式(详解)
2017/06/07 PHP
PHP获取访问设备信息的方法示例
2019/02/20 PHP
PHP进阶学习之Geo的地图定位算法详解
2019/06/19 PHP
解决IE下select标签innerHTML插入option的BUG(兼容IE,FF,Opera,Chrome,Safari)
2010/05/13 Javascript
javascript日期转换 时间戳转日期格式
2011/11/05 Javascript
使用jquery mobile做幻灯播放效果实现步骤
2013/01/04 Javascript
写得不错的jquery table鼠标经过变色代码
2013/09/27 Javascript
jquery阻止后续事件只执行第一个事件
2014/07/24 Javascript
APP中javascript+css3实现下拉刷新效果
2016/01/27 Javascript
JS修改地址栏参数实例代码
2016/06/14 Javascript
手把手教你把nodejs部署到linux上跑出hello world
2017/06/19 NodeJs
js实现左右两侧浮动广告
2018/07/09 Javascript
Angular6 写一个简单的Select组件示例
2018/08/20 Javascript
vue-cli3脚手架的配置及使用教程
2018/08/28 Javascript
在vue-cli的组件模板里使用font-awesome的两种方法
2018/09/28 Javascript
微信小程序自定义toast弹窗效果的实现代码
2018/11/15 Javascript
jquery的$().each和$.each的区别
2019/01/18 jQuery
使用Vue实现移动端左滑删除效果附源码
2019/05/16 Javascript
JavaScript对象原型链原理详解
2020/02/05 Javascript
Postman参数化实现过程及原理解析
2020/08/13 Javascript
vue+element_ui上传文件,并传递额外参数操作
2020/12/05 Vue.js
[02:44]DOTA2英雄基础教程 魅惑魔女
2014/01/07 DOTA
Python中内建函数的简单用法说明
2016/05/05 Python
详解Python3注释知识点
2019/02/19 Python
Python拆分大型CSV文件代码实例
2019/10/07 Python
主要的Ajax框架都有什么
2013/11/14 面试题
优秀实习自我鉴定
2013/12/04 职场文书
餐饮业会计岗位职责
2013/12/19 职场文书
教师求职信范文分享
2013/12/27 职场文书
2015年安全月活动总结
2015/03/26 职场文书
《乘法分配律》教学反思
2016/02/24 职场文书
Promise面试题详解之控制并发
2021/05/14 面试题
帮你提高开发效率的JavaScript20个技巧
2021/06/18 Javascript
面试官问我Mysql的存储引擎了解多少
2022/08/05 MySQL
Mysql的Table doesn't exist问题及解决
2022/12/24 MySQL