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 相关文章推荐
对 lightbox JS 图片控件进行了一下改造, 使其他支持复杂的图片说明
Mar 20 Javascript
读jQuery之十三 添加事件和删除事件的核心方法
Aug 23 Javascript
javascript定时变换图片实例代码
Mar 17 Javascript
node.js中的console.info方法使用说明
Dec 09 Javascript
js获取当前日期前七天的方法
Feb 28 Javascript
JQuery判断radio(单选框)是否选中和获取选中值方法总结
Apr 15 Javascript
jQuery异步上传文件插件ajaxFileUpload详细介绍
May 19 Javascript
基于JavaScript判断浏览器到底是关闭还是刷新(超准确)
Feb 01 Javascript
Vuejs第六篇之Vuejs与form元素实例解析
Sep 05 Javascript
微信小程序onLaunch异步,首页onLoad先执行?
Sep 20 Javascript
layui表单提交到后台自动封装到实体类的方法
Sep 12 Javascript
在JavaScript中如何使用宏详解
May 06 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
动画 《Pokemon Sword·Shield》系列WEB动画《薄明之翼》第2话声优阵容公开!
2020/03/06 日漫
PHP 编写大型网站问题集
2010/05/07 PHP
PHP程序漏洞产生的原因分析与防范方法说明
2014/03/06 PHP
Zend Framework+smarty用法实例详解
2016/03/19 PHP
PHP二分查找算法示例【递归与非递归方法】
2016/09/29 PHP
PHP实现chrome表单请求数据转换为接口使用的json数据
2021/03/04 PHP
php+js实现倒计时功能
2014/06/02 Javascript
Javascript 绘制 sin 曲线过程附图
2014/08/21 Javascript
node.js+Ajax实现获取HTTP服务器返回数据
2014/11/26 Javascript
javascript获取四位数字或者字母的随机数
2015/01/09 Javascript
推荐一个自己用的封装好的javascript插件
2015/01/29 Javascript
通过jquery-ui中的sortable来实现拖拽排序的简单实例
2016/05/24 Javascript
浅谈jquery的map()和each()方法
2016/06/12 Javascript
Angular2学习笔记之数据绑定的示例代码
2018/01/03 Javascript
JS document文档的简单操作完整示例
2020/01/13 Javascript
Nuxt的动态路由和参数校验操作
2020/11/09 Javascript
[51:34]Ti4主赛事胜者组 DK vs EG 2
2014/07/19 DOTA
pyv8学习python和javascript变量进行交互
2013/12/04 Python
python中cPickle用法例子分享
2014/01/03 Python
Python中的Matplotlib模块入门教程
2015/04/15 Python
Python中的hypot()方法使用简介
2015/05/18 Python
解决Tensorflow安装成功,但在导入时报错的问题
2018/06/13 Python
python中map的基本用法示例
2018/09/10 Python
python斐波那契数列的计算方法
2018/09/27 Python
Pycharm无法使用已经安装Selenium的解决方法
2018/10/13 Python
Linux下Pycharm、Anaconda环境配置及使用踩坑
2018/12/19 Python
Keras设定GPU使用内存大小方式(Tensorflow backend)
2020/05/22 Python
Python连接mysql数据库及简单增删改查操作示例代码
2020/08/03 Python
HTML5中input[type='date']自定义样式与日历校验功能的实现代码
2017/07/11 HTML / CSS
快速实现一个简单的canvas迷宫游戏的示例
2018/07/04 HTML / CSS
实习教师个人的自我评价
2013/11/08 职场文书
应届生新闻编辑求职信
2013/11/19 职场文书
大学生暑期实践感言
2014/02/26 职场文书
大学生十八大感想
2015/08/11 职场文书
《蟋蟀的住宅》教学反思
2016/02/17 职场文书
openstack中的rpc远程调用的方法
2021/07/09 Python