使用JavaScript浅谈组合模式
什么是组合模式?
从前有座山,山上住着一个老和尚和小和尚,老和尚给小和尚讲了一个故事,这个故事是这样子的:从前有座山,山上住着一个老和尚和小和尚,老和尚给小和尚讲了一个故事,这个故事是这样子的:从前有座山,山上住着一个老和尚和小和尚,老和尚给小和尚讲了一个故事,这个故事是这样子的。。。
骚年,读到这里不知道你明白了什么,一个故事由另一个故事组成,然后一直这样子组合下去,形成了一个最大的故事。
没错,这就是组合模式:小对象组合成一个大对象,小对象可能由更小的对象组合而成。
其实我们也可以看出两点:
1.我们只要最大的对象开始执行相关操作,用户就不需要关心下面的子对象的相关操作(会自动执行)也就是每一个对象要有统一的执行接口(一般就是相同的方法名称)。
2.这样子一直组合,我们的系统是否能承受的了。(就要对整体进行把握了)。
我们在电脑中搜索文件的时候,我们有可能搜索的是单个文件,有可能是一个文件夹,也有可能是从一个盘符开始搜索的。其实文件在电脑里面的组合方式,就是典型的组合模式。一个文件夹我们不管里面是有其他文件还是有其他文件,对吧,我们只要在这个文件夹中进行扫描,如果文件存在就一定会存在的。
我们来看一下代码实现:
// 定义文件夹 class Folder { constructor(name) { this.name = name; this.files = []; } // 添加文件/文件夹 add(file) { this.files.push(file); } // scan扫描文件 scan() { for (let i = 0, file, files = this.files; file = files[i++];) { // 子文件/子文件夹执行扫描 file.scan(); } } } // 定义文件 class File { constructor(name) { this.name = name; } scan() { console.log('扫描到文件:' + this.name); } } var folder = new Folder('最大的文件夹'); // 向最大的文件夹添加文件 folder.add(new File('渣女的养成记')); folder.add(new File('论如何学会享受孤独')); // 新建文件夹 var folder1 = new Folder('案例集合'); // 向新文件夹添加文件 folder1.add(new File('他日若遂凌云志,敢笑黄巢不丈夫')); folder1.add(new File('深入浅出学vue')); folder1.add(new File('git学习指南')); // 把新文件夹加入到最大的文件夹 folder.add(folder1); // 开始扫描 folder.scan();
在这里我们定义了文件夹和文件类,注意他们都有一个统一的接口scan,只要执行最大文件夹的scan方法,我们就不需要关心其他子文件夹和子文件的执行,他们会自动执行他们的scan方法,是不是很方便。
但是我们这里会发现一个问题,那就是file只是一个文件,我们在电脑中向一个文件中添加一个文件是否提示错误的,我们在这里并没有做出控制,如果file执行add操作,那么会报错,这里我们友好的提示一下用户:
// 定义文件夹 class Folder { constructor(name) { this.name = name; this.files = []; } // 添加文件/文件夹 add(file) { this.files.push(file); } // scan扫描文件 scan() { for (let i = 0, file, files = this.files; file = files[i++];) { // 子文件/子文件夹执行扫描 file.scan(); } } } // 定义文件 class File { constructor(name) { this.name = name; } scan() { console.log('扫描到文件:' + this.name); } add() { throw new Error('文件下面不能再添加文件'); } } var folder = new Folder('最大的文件夹'); // 向最大的文件夹添加文件 folder.add(new File('渣女的养成记')); folder.add(new File('论如何学会享受孤独')); // 新建文件夹 var folder1 = new Folder('案例集合'); // 向新文件夹添加文件 folder1.add(new File('他日若遂凌云志,敢笑黄巢不丈夫')); folder1.add(new File('深入浅出学vue')); folder1.add(new File('git学习指南')); // 把新文件夹加入到最大的文件夹 folder.add(folder1); // 开始扫描 folder.scan(); var testFile = new File('测试文件'); testFile.add(new File('新的文件'));
现在我们对其做了限制。
我们设想一下我们有时候是不是会删除文件,删除文件之后,那么之前它的scan方法就不会在执行了。
但是删除好说,我们要想到这样实现删除了,我们需要怎样删除了。我们的代码中,文件是不是都被保存在文件中,只要找到这个文件夹,我们根据文件名称,是不是就可以执行删除操作了。所以说我们在文件夹执行添加操作的时候,就要把新增文件的所在的文件夹的引用添加到这个文件中。
我们看一下代码:
// 定义文件夹 class Folder { constructor(name) { this.name = name; this.files = []; } // 添加文件/文件夹 add(file) { this.files.push(file); file.parent = this; } // scan扫描文件 scan() { for (let i = 0, file, files = this.files; file = files[i++];) { // 子文件/子文件夹执行扫描 file.scan(); } } remove() { if (!this.parent) { //根节点或者树外的游离节点 return; } for (let files = this.parent.files, l = files.length; l >= 0; l--) { let file = files[l]; if (this === file) { files.splice(l, 1); } } } } // 定义文件 class File { constructor(name) { this.name = name; this.parent = null; } scan() { console.log('扫描到文件:' + this.name); } add() { throw new Error('文件下面不能再添加文件'); } remove() { if (!this.parent) { //根节点或者树外的游离节点 return; } for (let files = this.parent.files, l = files.length; l >= 0; l--) { let file = files[l]; if (this === file) { files.splice(l, 1); } } } } var folder = new Folder('最大的文件夹'); // 向最大的文件夹添加文件 folder.add(new File('渣女的养成记')); folder.add(new File('论如何学会享受孤独')); // 新建文件夹 var folder1 = new Folder('案例集合'); // 向新文件夹添加文件 folder1.add(new File('他日若遂凌云志,敢笑黄巢不丈夫')); folder1.add(new File('深入浅出学vue')); folder1.add(new File('git学习指南')); // 把新文件夹加入到最大的文件夹 folder.add(folder1); // 开始扫描 folder.scan(); // 删除文件夹 console.log('---'); folder1.remove(); folder.scan();
现在我们实现了删除操作,测试如下:
还是可以的。