zoukankan      html  css  js  c++  java
  • 再起航,我的学习笔记之JavaScript设计模式15(组合模式)

    组合模式

    组合模式(Composite): 又称部分-整体模式,将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
    如果有一个需求需要我们做一个门户网站,涉及到门户网站自然离不开文章列表,那么文章列表里又有文字列表,图片列表,图文列表,这么多种类的文章列表我们该怎么做呢?
    首先我们可以创建一个文章列表的抽象父类,让其他的不同类型的文章列表都继承于这个类

    var articles=function(){
    	//子组件容器
    	this.children=[];
    	//当前组件元素
    	this.element=null;
    }
    articles.prototype={
    	init:function(){
    		throw new Error("方法必须重写!");
    	},
    	add:function(){
    		throw new Error("方法必须重写!");
    	},
    	getElement:function(){
    		throw new Error("方法必须重写!");
    	}
    }
    

    我们定义了这个接口抽象父类,下一步我们就是要实现所有的子类,但是我们要注意他们的层次关系,因为组合模式不仅仅是单层次组合,也可以是一个多层次的。
    接着我们定义一个文章列表模块容器类

    var Container=function(id,parent){
    	//构造函数继承父类
    	articles.call(this);
    	//模块ID
    	this.id=id;
    	//模块的父容器
    	this.parent=parent;
    	//构建初始化方法
    	this.init();
    }
    

    原型式继承

    function proObject(o){
        //声明一个过渡函数对象
        function Obj(){}
        //过渡对象的原型继承父对象
        Obj.prototype=o;
        //返回过渡对象的一个实例,该实例的原型继承了父对象
        return new Obj();
    }
    

    寄生式组合式继承父类原型方法

    function parasiticType(ParentClass,ChildClass){
        //复制一份父类的原型保存在变量中
         var parent=proObject(ParentClass.prototype);
         //修正因为重写子类原型导致子类的constructor属性被修改
         parent.constructor=ChildClass;
         //设置子类的原型
         ChildClass.prototype=parent;
    }
    
    parasiticType(articles,Container);
    

    接着我们实现之前的初始化方法

    Container.prototype.init=function(){
    	this.element=document.createElement('ul');
    	this.element.id=this.id;
    	this.element.className='articles-container'
    }
    

    现在我们实现抽象父类中的方法

    Container.prototype.add=function(child){
    	console.log(child);
    	//在子元素容器中插入子元素
    	this.children.push(child);
    	//插入当前组件元素树中
    	this.element.appendChild(child.getElement());
    	return this;
    }
    
    Container.prototype.getElement=function(){
    	return this.element;
    }
    Container.prototype.show=function(){
    	this.parent.appendChild(this.element);
    }
    

    同样的下一层级的行成员集合类实现方法与之类似。

    var Item=function(className){
    	articles.call(this);
    	this.className=className||"";
    	this.init();
    }
    parasiticType(articles,Item);
    Item.prototype.init=function(){
    	this.element=document.createElement('li');
    	this.element.className=this.className;
    }
    Item.prototype.add=function(child){
    	console.log(child);
    	//在子元素容器中插入子元素
    	this.children.push(child);
    	//插入当前组件元素树中
    	this.element.appendChild(child.getElement());
    	return this;
    }
    Item.prototype.getElement=function(){
    	return this.element;
    }
    

    文章组合实体类实现方法也是一样的

    var articlesGroup=function(className){
    	articles.call(this);
    	this.className=className||"";
    	this.init();
    }
    parasiticType(articles,articlesGroup);
    articlesGroup.prototype.init=function(){
    	this.element=document.createElement('div');
    	this.element.className=this.className;
    }
    articlesGroup.prototype.add=function(child){
    	//在子元素容器中插入子元素
    	this.children.push(child);
    	//插入当前组件元素树中
    	this.element.appendChild(child.getElement());
    	return this;
    }
    articlesGroup.prototype.getElement=function(){
    	return this.element;
    }
    

    好了现在我们已经把所有子类成员创建出来了,不过光有这些容器类还不行,还需要有更底层的文章列表类。
    图片文章列表

    var ImageArticles=function(url,href,className){
    	articles.call(this);
    	this.url=url||"";
    	this.href=href||"#";
    	this.className=className||"normal";
    	this.init();
    }
    parasiticType(articles,ImageArticles);
    ImageArticles.prototype.init=function(){
    	this.element=document.createElement('a');
    	var img=new Image();
    	img.src=this.url;
    	this.element.appendChild(img);
    	this.element.className='image-news'+this.className;
    	this.element.href=this.href;
    }
    ImageArticles.prototype.add=function(){}	
    ImageArticles.prototype.getElement=function(){
    		return this.element;
    	}
    

    文字文章列表

    var TextArticles=function(text,href){
    	articles.call(this);
    	this.text=text||"";
    	this.href=href||"#";
    	this.init();
    }
    parasiticType(articles,TextArticles);
    TextArticles.prototype.init=function(){
    	this.element=document.createElement('a');
    	this.element.innerHTML=this.text;
    	this.element.href=this.href;
    	this.element.className="text";
    }
    TextArticles.prototype.add=function(){}
    TextArticles.prototype.getElement=function(){
    		return this.element;
    	}
    
    

    现在所有的类我们都已经创建了,现在我们就要通过add方法来组合这些类

    var Articles=new Container('articles',document.body);
    Articles.add(new Item('normal').add(
    	new ImageArticles('img/HBuilder.png','#','small')
    )
    .add(new Item('normal').add(
    	new ImageArticles('img/HBuilder.png','#','small')
    )
    )
    .add(new Item('normal').add(
    	new TextArticles('测试列表','#')
    	)
    )
    .add(new Item('normal').add(
    	new TextArticles('测试列表2','#')
    	)
    )
    ).show();
    

    我们来看一看效果,这里我就偷懒不写样式了。我们可以看到,列表已经颇具雏形了。

    现在我们创建的每一条文章列表都是一个独立的个体,互不影响,避免相互间的耦合,也增强了组合后的模块的复杂性,如果需要混搭,我们做出相应的组合就可以轻易的完成

    总结

    组合模式能够给我们提供一个清晰的组成结构。组合对象类通过继承同一个父类使其具有统一的方法,这样也方便了我们统一管理与使用,当然此时单体成员与组合体成员行为表现就比较一致了。这也就模糊了简单对象与组合对象的区别。有时这也是一种对数据的分级式处理。清晰而又方便我们队数据的管理与使用。

    也谢谢大家看到这里:)如果你觉得我的分享还可以请点击推荐,分享给你的朋友让我们一起进步~

    好了以上就是本次分享的全部内容,本次示例参考自JavaScript设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。

    欢迎转载,转载请注明作者,原文出处。

  • 相关阅读:
    封装
    魔术方法类与有关面向对象的关键字
    JS基础
    轮播效果
    进度条效果
    2018年6月
    2018年5月
    Monte Carlo tree search 学习
    2018年4月
    pachi 学习
  • 原文地址:https://www.cnblogs.com/chen-jie/p/JavaScript-Composite.html
Copyright © 2011-2022 走看看