JavaScript 设计模式之享元模式

发布于 2021-11-16 12:46:07 字数 5860 浏览 1204 评论 0

四个轮子,一个方向盘,有刹车,油门,车窗,这些词首先让人联想到的就是一辆汽车。的确,这些都是是一辆车的最基本特征,或者是属性,我们把词语抽象出来,而听到这些词语的人把他们想象陈一辆汽车。在代码里面也是这样的,为了将所有的车辆统一描述,我们将车的特征抽象出来,作为一个理念,就像柏拉图说的那样,接下来,我们要这个理念描绘他,让人来认识。这也就是程序中的实例化:

function Car() {
	this.lunzi = 4;
	this.shache = 1;
}
//js 中没有类概念,但是我们可以模拟出来。这样,Car 就作为了构造函数了。

接下来我们来实例它,让柏拉图的理念在现象世界中得到展示:

var benchi = new Car();

benchi 这个变量就是一个实例了。作为一辆汽车,它有四个轮子(lunzi),一个刹车(shache)。但是问题来了:理念只有一个,但是实例有很多,如果实例太多会怎么样呢?就跟起初上帝造物一样,起初只有一个人,后来从亚当身上撤下肋骨一根做了夏娃,夏娃吃了禁果,人类繁衍,人一多,罪恶就多了。

程序的世界里也是这样,对象实例化次数过多,会导致内存无限制的增长,性能可想而知。那么怎么样避免这样的情况出现呢?享元模式出现了。下面我们以一个地方的财政系统购买外国车辆为例,运用享元模式解答难题,这个传统的实现方法:

var Cars = function(id, lt, fxp, lhq, sc, zw, cp, pp, ys, jg, cc, dhy ) {
    this.id = id;
    this.lt = lt;//轮胎
    this.fxp = fxp;//方向盘
    this.lhq = lhq;//离合器
    this.sc = sc;//刹车
    this.zw = zw;//座位
    this.cp = cp;//车牌
    this.pp = pp;//品牌
    this.ys = ys;//颜色
    this.jg = jg;//价格
    this.cc = d.cc;//车窗
    this.dhy = d.dhy;//导航仪
}

接下来的事情很容易,一个一个实例化这个类:

var car 1 = new Car('x',x,x,x,x,x,x,x,x,x);
var car 2 = new Car('x',x,x,x,x,x,x,x,x,x);
var car 3 = new Car('x',x,x,x,x,x,x,x,x,x);
...............................
var car 100 = new Car('x',x,x,x,x,x,x,x,x,x);//终于完了!!!

但是车辆数量一多起来的话会出现内存过多的问题。我们可以看到,一辆车里面有很多属性比如刹车数量,车窗数量,座位数量是相同的,享元模式的核心思想就是把共同享有的基本元素抽象出来。来看看是如何实现的。

//享元模式
    var flyWight  = (function(window, undefined){//立即执行函数返回一个享元对象
        var Cars = {};//这里是存储器,用存储所有的car对象
        var Car = function(d) {//构造方法
            this.id = d.id;
            this.lt = d.lt;//轮胎
            this.fxp = d.fxp;//方向盘
            this.lhq = d.lhq;//离合器
            this.sc = d.sc;//刹车
            this.zw = d.zw;//座位
            this.cc = d.cc;//车窗
            this.dhy = d.dhy;//导航仪
        }
        Car.prototype.gmg = 'china';// 买家的国籍,这里适应定义一些公共属性,适用于所有车辆的公用属性,比如购买的国家,方便统一修改
        Car.prototype = {//Car的原型类方法,上面提供了操作Car实例的一些方法,可以自己定义,卤煮在此只定义最基本的方法
            set : function(n ,v) {
                this[n] = v;
            },
            get : function(n) {
                return this[v];
            }
        }
        var _factory = function(d) {//构建实例工厂,以pp(品牌)为类构建Car类别,,此方法属于私有,外部无法调用。
            if(!Cars[d.pp]) {
                Cars[d.pp] = new Car(d);
            }
            return Cars[d.pp];//返回该品牌的类别
        }
 
        var controlCar =  {//享元对象
            allCars : {},//另一个存储所有车辆的对象,
            addCar : function(data) {//添加一辆车
                if(this.allCars[data.id]) return this.allCars[data.id];
                this.allCars[data.id] = {
                    id : data.id,
                    pp : data.pp,
                    ys : data.ys,
                    cp : data.cp,
                    buyTime : data.buyTime,
                    jg : data.jg,
                    car : _factory(data)
                }
            },
            removeCar : function(data) {//删除一辆车
                if(this.allCars[data.id]) {
                    this.allCars[data.id] = undefined;
                }
            },
            getCar : function (id) {//获取一辆汽车
                return this.allCars[id];
            }
        }
 
        return controlCar;//返回享元对象
    })(window, undefined);

从以上的代码我们可以看到,利用汽车的品牌作为实例的对象而不是单一的某个汽车。因为品牌再多是不会超过汽车的总数的,所以我们实际上要实例的对象就是十几个甚至是几个对象而已。

这样,在allCar所有的子集元素中,每一个都是一辆包含有品牌类别的对象字面量而不是Car的实例对象了,我们可以在Car的原型链中中编写很多方法操作它们,同时它(该对象)不会丧失作为一辆汽车的基本特征,而且还会保持自己私有(颜色,价格)等特征。

这样我们就达到了减少实例对象的目的了。下面,我们来看看它们是是怎么被用到的。首先是数据,这里有八辆车,三个品牌的,所以只有有三个 Car 的实例对象:

var data = [
    {id:1,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x1231',pp:'benchi', ys:'red', jg:200000, buyTime : '1999-08-20', cc : 6, dhy : 1},
    {id:2,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x1232',pp:'benchi', ys:'black', jg:207000, buyTime : '1999-08-21', cc : 6, dhy : 1},
    {id:3,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x1233',pp:'benchi', ys:'gray', jg:200000, buyTime : '1999-08-22', cc : 6, dhy : 1},
 
    {id:4,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x4564',pp:'fute', ys:'yellow', jg:150000, buyTime : '1999-08-23', cc : 6, dhy : 1},
    {id:5,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x4565',pp:'fute', ys:'black', jg:160000, buyTime : '1999-08-24', cc : 6, dhy : 1},
    {id:6,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x4566',pp:'fute', ys:'gray', jg:109000, buyTime : '1999-08-25', cc : 6, dhy : 1},
 
    {id:7,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x7897',pp:'baoma', ys:'black', jg:380000, buyTime : '1999-08-26', cc : 6, dhy : 1},
    {id:8,lt:4,fxp:1,lhq:1,sc:1,zw:5,cp:'x7898',pp:'baoma', ys:'blue', jg:300000, buyTime : '1999-08-27', cc : 6, dhy : 1}
]

轮询通过模式封装:

for(var i=0; i<data.length; i++) {
    flyWight.addCar(data[i]);
}

通过 flyWight.getCar(id) 你可以获得你想要的某一辆车,你可以用直接赋值的方法修改它的特殊属性;

flyWight.getCar('1').jg = 300000

也可以通过 Car 字段所包含的对象的私有方法处理一些实际上很少会改变的值:

flyWight.getCar('1').car.set('lt', 5)

当然,我们在这里的基础属性并不是私有的,你可以按照自己的想法来编写代码。

总结:享元模式的核心:

  1. 有基础单元共享的数据对象集合。
  2. 构造工厂函数。
  3. 存储实例对象的容器。

你可以在基本思想上面发挥自己的想法,这里例举的只是一个很简单的例子。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

甜柠檬

暂无简介

0 文章
0 评论
19202 人气
更多

推荐作者

已经忘了多久

文章 0 评论 0

15867725375

文章 0 评论 0

LonelySnow

文章 0 评论 0

走过海棠暮

文章 0 评论 0

轻许诺言

文章 0 评论 0

信馬由缰

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文