前言

本系列文章主要根据《JavaScript设计模式与开发实践》整理而来,其中会加入了一些自己的思考。希望对大家有所帮助。

概念

组合模式就是用小的子对象来构建更大的对象,而这些小的子对象本身也许是由更小的“孙对象”构成的。

场景

组合模式除了要求组合对象和叶对象拥有相同的接口之外,还有一个必要条件,就是对一组叶对象的操作必须具有一致性。

优缺点

优点

组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构。 除了用来表示树形结 构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使 用具有一致性

缺点

它可能会产生一个这样的系统:系统中的每个对象看起来都 与其他对象差不多。它们的区别只有在运行的时候会才会显现出来,这会使代码难以理解。此外, 如果通过组合模式创建了太多的对象,那么这些对象可能会让系统负担不起。

例子

扫描文件夹

这里把文件和文件夹无区别对待,文件和文件夹都具有add和scan方法

var Folder = function (name) {
  this.name = name;
  this.files = [];
};
Folder.prototype.add = function (file) {
  this.files.push(file);
};
Folder.prototype.scan = function () {
  console.log('开始扫描文件夹: ' + this.name);
  for (var i = 0, file, files = this.files; file = files[i++];) {
    file.scan();
  }
};
/******************************* File ******************************/
var File = function (name) {
  this.name = name;
};
File.prototype.add = function () {
  throw new Error('文件下面不能再添加文件');
};
File.prototype.scan = function () {
  console.log('开始扫描文件: ' + this.name);
};

var folder = new Folder('学习资料');
var folder1 = new Folder('JavaScript');
var folder2 = new Folder('jQuery');
var file1 = new File('JavaScript 设计模式与开发实践');
var file2 = new File('精通jQuery');
var file3 = new File('重构与模式')
folder1.add(file1);
folder2.add(file2);
folder.add(folder1);
folder.add(folder2);
folder.add(file3);

var folder3 = new Folder('Nodejs');
var file4 = new File('深入浅出Node.js');
folder3.add(file4);
var file5 = new File('JavaScript 语言精髓与编程实践');

folder.add(folder3);
folder.add(file5);

folder.scan();

增加删除方法

我们需要增加一个父对象的引用

var Folder = function (name) {
  this.name = name;
  this.parent = null; //增加this.parent 属性
  this.files = [];
};

Folder.prototype.add = function (file) {
  file.parent = this; //设置父对象
  this.files.push(file);
};

Folder.prototype.scan = function () {
  console.log('开始扫描文件夹: ' + this.name);
  for (var i = 0, file, files = this.files; file = files[i++];) {
    file.scan();
  }
};

Folder.prototype.remove = function () {
  if (!this.parent) { //根节点或者树外的游离节点
    return;
  }
  for (var files = this.parent.files, l = files.length - 1; l >= 0; l--) {
    var file = files[l];
    if (file === this) {
      files.splice(l, 1);
    }
  }
};

var File = function (name) {
  this.name = name;
  this.parent = null;
};

File.prototype.add = function () {
  throw new Error('不能添加在文件下面');
};

File.prototype.scan = function () {
  console.log('开始扫描文件: ' + this.name);
};

File.prototype.remove = function () {
  if (!this.parent) { //根节点或者树外的游离节点
    return;
  }

  for (var files = this.parent.files, l = files.length - 1; l >= 0; l--) {
    var file = files[l];
    if (file === this) {
      files.splice(l, 1);
    }
  }
};

var folder = new Folder('学习资料');
var folder1 = new Folder('JavaScript');
var file1 = new Folder('深入浅出Node.js');

folder1.add(new File('JavaScript 设计模式与开发实践'));
folder.add(folder1);
folder.add(file1);
folder1.remove(); //移除文件夹
folder.scan();