再起航,我的学习笔记之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设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。

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

基于51单片机,实现对直流电机的调速、测速以及正反转控制。项目包含完整的仿真文件、源程序、原理图和PCB设计文件,适合学习和实践51单片机在电机控制方面的应用。 功能特点 调速控制:通过按键调整PWM占空比,实现电机的速度调节。 测速功能:采用霍尔传感器非接触式测速,实时显示电机转速。 正反转控制:通过按键切换电机的正转和反转状态。 LCD显示:使用LCD1602液晶显示屏,显示当前的转速和PWM占空比。 硬件组成 主控制器:STC89C51/52单片机(与AT89S51/52、AT89C51/52通用)。 测速传感器:霍尔传感器,用于非接触式测速。 显示模块:LCD1602液晶显示屏,显示转速和占空比。 电机驱动:采用双H桥电路,控制电机的正反转和调速。 软件设计 编程语言:C语言。 开发环境:Keil uVision。 仿真工具:Proteus。 使用说明 液晶屏显示: 第一行显示电机转速(单位:转/分)。 第二行显示PWM占空比(0~100%)。 按键功能: 1键:加速键,短按占空比加1,长按连续加。 2键:减速键,短按占空比减1,长按连续减。 3键:反转切换键,按下后电机反转。 4键:正转切换键,按下后电机正转。 5键:开始暂停键,按一下开始,再按一下暂停。 注意事项 磁铁和霍尔元件的距离应保持在2mm左右,过近可能会在电机转动时碰到霍尔元件,过远则可能导致霍尔元件无法检测到磁铁。 资源文件 仿真文件:Proteus仿真文件,用于模拟电机控制系统的运行。 源程序:Keil uVision项目文件,包含完整的C语言源代码。 原理图:电路设计原理图,详细展示了各模块的连接方式。 PCB设计:PCB布局文件,可用于实际电路板的制作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值