JavaScript设计模式(6)—— 结构型设计模式【2】

原文出自于本人个人博客网站:https://www.dzyong.com(欢迎访问)

转载请注明来源: 邓占勇的个人博客 - 《JavaScript设计模式(6)—— 结构型设计模式【2】

本文链接地址: https://www.dzyong.com/#/ViewArticle/91

设计模式系列博客:JavaScript设计模式系列目录(持续更新中)

 

在上一篇博客中介绍了结构型设计模式的前4种:外观模式、适配器模式、代理模式和装饰者模式。

接下来介绍剩下的3种结构型设计模式:桥接模式、组合模式和享元模式。

桥接模式

在系统沿着多个维度变化的同时,又不增加其复杂度并已达到解耦。

看下面这个案例,当鼠标滑过时,对于项目来说需要使整个的样式改变,对于消息来说,只需要对数字的样式改变即可。

HTML部分如下:

<span>项目1</span>
<span>消息<strong>9</strong></span>

如果是传统写法一般是这样写:

let spans = document.getElementsByTagName('span')
spans[0].onmouseover = function(){
    this.style.color = 'red'
    this.style.background = 'pink'
}
spans[0].onmouseout = function(){
    this.style.color = 'black'
    this.style.background = '#fff'
}
spans[1].onmouseover = function(){
    this.getElementsByTagName('strong')[0].style.color = 'red'
    this.getElementsByTagName('strong')[0].style.background = 'pink'
}
spans[1].onmouseout = function(){
    this.getElementsByTagName('strong')[0].style.color = 'black'
    this.getElementsByTagName('strong')[0].style.background = '#fff'

这看起来似乎显得很繁琐,存在许多共同的部分需要重复的编写。我可以对其进行改进。

首先可以抽象提取出他们的共同部分,如改变样式:

/*提取共同点*/
let changecolor = function(dom, color, bg){
    dom.style.color = color
    dom.style.background = bg
}

接下来就是时事件绑定,但是需要明白一点,仅仅知道元素事件绑定与抽象提取的设置样式方法是不够的,你需要一个方法将他们链接起来,这个方法就是桥接方法,这种模式就是桥接模式。

let spans = document.getElementsByTagName('span')
spans[0].onmouseover = function(){
    changecolor(this, 'red', 'pink')
}
spans[0].onmouseout = function(){
    changecolor(this, 'black', '#fff')
}
spans[1].onmouseover = function(){
    changecolor(this.getElementsByTagName('strong')[0], 'red', 'pink')
}
spans[1].onmouseout = function(){
    changecolor(this.getElementsByTagName('strong')[0], 'black', '#fff')
}

不过桥接模式的强大之处不仅仅在此,甚至对于多维的变化也同样使用。

比如我们书写一个canvas跑步游戏的时候,对于游戏中的人、精灵、小球等一系列的实物都有动作单元,而他们的动作实现起来的方式又都是统一的,比如人、精灵和球的运动其实就是位置坐标x和y的变化,球的颜色与精灵的色彩的绘制方式都相似等。这种我们可以将这些多维变化部分,提取出来作为一个抽象运动单元进行保存,而当我们创建实体时,将需要的每个抽象动作作为单元通过桥接,链接在一起运作。这样他们之间不会相互影响并且该方式降低了他们之间的耦合。

//运动单元
let Speed = function(x, y){
    this.x = x
    this.y = y
}
Speed.prototype.run = function(){
    console.log('跑起来')
}
//着色单元
let Color = function(c){
    this.color = c
}
Color.prototype.draw = function(){
    console.log('上色')
}
//变形单元
let Shape = function(sp){
    this.shape = sp
}
Shape.prototype.change = function(){
    console.log('改变形状')
}
//说话单元
let Speek = function(wd){
    this.word = wd
}
Speek.prototype.say = function(){
    console.log('说话')
}

//创建小球类
let Ball = function(x, y, c){
    this.speed = new Speed(x, y)
    this.color = new Color(c)
}
//初始化
Ball.prototype.init = function(){
    this.speed.run()
    this.color.draw()
}

let b = new Ball(2, 2, 'red')
b.init()

组合模式

又称部分-整体模式,将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

 

享元模式

运用共享技术有效地支持大量的细粒度的对象,避免对象间拥有相同内容造成多余的开销。

例子:有一个新闻模块,新闻数量较多,因此需要进行分页展示。一种简便的方法就是先请求获取所有的新闻数据,并全部添加到一个div盒子中,根据所选择的页码不同,对相应的新闻进行显示隐藏。

上面这种做法存在一个很严重的问题,所有的新闻模块结构是相同的,只是内容不同。如果一次性把所有的新闻同时插入页面中的操作会造成多余的开销,在低版本的IE浏览器中会严重影响性能。

对于这种情况就可以采用享元模式,主要对其数据、方法共享分离,它将数据和方法分成内部数据、内部方法和外部数据、外部方法。内部数据与内部方法指的是相似或者共有的数据和方法,所以这一部分要提取出来减少开销,以提高性能。

let Flyweight = function(){
    //已创建的元素
    var created = []
    //创建一个新闻包装容器
    function create(){
        var dom = document.createElement('div')
        document.getElementById('container').appendChild(dom)
        created.push(dom)
        return dom
    }
    return {
        //获取新闻元素方法
        getDiv: function(){
            //如果已创建的元素小于当前页数总个数,则创建
            if(created.length < 5)
                return create()
            else{
                //获取第一个元素,并插入最后
                var div = created.shift()
                created.push(div)
                return div
            }
        }
    }
}

内部数据和方法提取出来之后就要实现外部数据和外部方法,外部数据就是要显示的所有新闻内容,因为每个内容不一样,所以他们不能被共享。

let article = ['第1条内容','第2条内容','第3条内容','第4条内容','第5条内容','第6条内容','第7条内容','第8条内容','第9条内容','第10条内容','第11条内容','第12条内容','第13条内容','第14条内容','第15条内容','第16条内容',]
let paper = 0,
    num = 5,
    len = article.length
//添加5条新闻
for (let i = 0; i < 5; i++) {
    if(article[i])
        Flyweight().getDiv().innerHTML = article[i] 
}
//下一页按钮绑定事件
document.getElementById('next').onclick = function(){
    //如果新闻内容不足5条则返回
    if(article.length < 5)
        return
    document.getElementById('container').innerHTML = ''
    var n = ++paper * num % len,   //获取当前页的第一条新闻索引
        j = 0
    //插入新闻
    for (;j < 5;j++) {
        //如果存在n + j 条则插入
        if(article[n + j])
            Flyweight().getDiv().innerHTML = article[n + j]
        //否则插入其实位置n + j - len条
        else if(article[n + j - len])
            Flyweight().getDiv().innerHTML = article[n + j - len]
        //如果不存在则插入空字符串
        else
            Flyweight().getDiv().innerHTML = ''
        
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端筱园

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值