您的位置:首页 > Web前端 > JavaScript

JS设计模式之享元模式

2013-10-23 00:34 411 查看
享元模式解决创建大量类似对象而导致的性能问题。

示例:

一个汽车类

//汽车登记示例
  var Car =function(make,model,year,owner,tag,renewDate){
    this.make=make;
    this.model=model;
    this.year=year;
    this.owner=owner;
    this.tag=tag;
    this.renewDate=renewDate;
  }
  Car.prototype = {
    getMake:function(){
      returnthis.make;
    },
    getModel:function(){
      returnthis.model;
    },
    getYear:function(){
      returnthis.year;
    },
    transferOwner:function(owner,tag,renewDate){
      this.owner=owner;
      this.tag=tag;
      this.renewDate=renewDate;
    },
    renewRegistration:function(renewDate){
      this.renewDate=renewDate;
    }
  }


拥有品牌、型号、出产日期三个内在属性,三个外在属性车主、车牌最近登记日期。

三个内在属性定义的组合,只需要一个对象即可。

新版Car类代码

//包含核心数据的Car类
  var Car=function(make,model,year){
    this.make=make;
    this.model=model;
    this.year=year;
  }
  Car.prototype={
    getMake:function
4000
(){
      returnthis.make;
    },
    getModel:function(){
      returnthis.model;
    },
    getYear:function(){
      returnthis.year;
    }
  }


用工厂模式实例化

//中间对象,用来实例化Car类
  var CarFactory=(function(){
    var createdCars = {};
    return {
      createCar:function(make,model,year){
if(createdCars[make+"-"+model+"-"+year]){
return  createdCars[make+"-"+model+"-"+year];
}else{
var car=new Car(make,model,year);
           createdCars[make +'-'+ model +'-'+ year] = car;
return car
}         
      }
    }
  })();

管理外在装态
//数据工厂,用来处理Car的实例化和整合附加数据
  var CarRecordManager = (function() {
    var carRecordDatabase = {};
    return {
      addCarRecord:function(make,model,year,owner,tag,renewDate){
        var car = CarFactory.createCar(make, model, year);
        carRecordDatabase[tag]={
          owner:owner,
          tag:tag,
          renewDate:renewDate,
          car:car
      }
    },
      transferOwnership:function(tag, newOwner, newTag, newRenewDate){
        var record=carRecordDatabase[tag];
        record.owner = newOwner;
        record.tag = newTag;
        record.renewDate = newRenewDate;
      },
      renewRegistration:function(tag,newRenewDate){
        carRecordDatabase[tag].renewDate=newRenewDate;
      },
      getCarInfo:function(tag){
        return carRecordDatabase[tag];
      }
    }
  })();

通过carRecordDatabase这个对象将内在对象与外在对象整合起来,享元对象与其所属的方法集中管理。相当于是哪一类型的车是一个对象,然后这个对象与其余的外在对象关联,减少对象的声明。

Web日历

不用享元模式,用组合模式包装,然后要创建365个CalendarDay对象,一年的话

var CalendarYear = function(year, parent){
this.year = year;
this.element = document.createElement('div');
this.element.style.display = 'none';
parent.appendChild(this.element);
function isLeapYear(y){
return (y>0)&&!(y%4)&&((y%100)||!(y%400));
}

this.months = [];
this.numDays = [31,isisLeapYear(this.year)?29:28,31,30,31,30,31,31,30,31,30,31];
for(var i = 0,len = 12; i<len; i++){
this.months[i] = new CalendarMonth(i, this.numDays[i], this.element);
}
};

CalendarYear.prototype = {
display:function(){
for(var i = 0, len = this.months.length; i<len; i++){
this.months[i].display();
}
this.element.style.dispaly = 'block';
}
};

var CalendarMonth = function(monthNum, numDays, parent){
this.monthNum = monthNum;
this.element = document.createElement('div');
this.element.style.display = 'none';
parent.appendChild(this.element);

this.days = [];
for(var i = 0, len = this.days.length; i < len; i++){
this.days[i] = new CalendarDay(i, this.element);
}
};

CalendarMonth.prototype = {
display:function(){
for(var i = 0, len = this.days.length; i<len; i++){
this.days[i].display();
}
this.element.style.dispaly = 'block';
}
};

var CalendarDay = function(date, parent){
this.date = date;
this.element = document.createElement('div');
this.element.style.display = 'none';
parent.appendChild(this.element);
};

CalendarDay.prototype = {
display: function(){
this.element.style.display = 'block';
this.element.innerHTML = this.date;
}
};


用享元模式

var CalendarDay = function(){};

CalendarDay.prototype = {
display: function(date, parent){
var element = document.createElement('div');
parent.appendChild(element);
element.innerHTML = date;
}
};

var calendarDay = new CalendarDay();

创建单个实例,以前的构造参数成为display的方法参数,然后修改CalendayMonth

var CalendarMonth = function(monthNum, numDays, parent){
this.monthNum = monthNum;
this.element = document.createElement('div');
this.element.style.display = 'none';
parent.appendChild(this.element);

this.days = [];
for(var i = 0, len = this.days.length; i < len; i++){
this.days[i] = calendarDay;
}
};

CalendarMonth.prototype = {
display:function(){
for(var i = 0, len = this.days.length; i<len; i++){
this.days[i].display(i, this.element);
}
this.element.style.dispaly = 'block';
}
};


将公用一个类,只是类的方法参数不同。这样内在数据只是calendarDay这个对象,外在对象日期等保存在组合对象的结构本身。

这样组合对象的叶对象只包含少量数据,就抽离出来,转化为共享资源。

var Tooltip = function(targetElement, text){
this.target = targetElement;
this.text = text;
this.delayTimeout = null;
this.delay = 1500;

this.element = document.createElement('div');
this.element.style.display = 'none';
this.element.style.position = 'absolute';
this.element.className = 'tooltip';
document.getElementsByTagName('body')[0].appendChild(this.element);
this.element.innerHTML = this.text;

var that = this;
addEvent(this.target, 'mouseover', function(e){that.startDelay(e);});
addEvent(this.target, 'mouseout', function(e){that.hide();});
};

Tooltip.prototype = {
startDelay:function(e){
if(this.delayTimeout == null){
var that = this;
var x = e.clientX;
var y = e.clientY;
this.delayTimeout = setTimeout(function(){
this.show(x,y);
},this.delay);
}
},
show:function(x, y){
clearTimeout(this.delayTimeout);
this.delayTimeout = null;
this.element.style.left = x + 'px';
this.element.style.top = y + 'px';
this.element.style.display = 'block';
},
hide:function(){
clearTimeout(this.delayTimeout);
this.delayTimeout = null;
this.element.style.display = 'none';
}
};


使用享元模式

var Tooltip = function(){
this.delayTimeout = null;
this.delay = 1500;

this.element = document.createElement('div');
this.element.style.display = 'none';
this.element.style.position = 'absolute';
this.element.className = 'tooltip';
document.getElementsByTagName('body')[0].appendChild(this.element);
};

Tooltip.prototype = {
startDelay:function(e, text){
if(this.delayTimeout == null){
var that = this;
var x = e.clientX;
var y = e.clientY;
this.delayTimeout = setTimeout(function(){
this.show(x, y, text);
},this.delay);
}
},
show:function(x, y, text){
clearTimeout(this.delayTimeout);
this.delayTimeout = null;
this.element.innerHTML = text;
this.element.style.left = x + 'px';
this.element.style.top = y + 'px';
this.element.style.display = 'block';
},
hide:function(){
clearTimeout(this.delayTimeout);
this.delayTimeout = null;
this.element.style.display = 'none';
}
};


享元对象,将外在数据先全部剔除。

var TooltipManager = (function(){
var storeInstance = null;
var Tooltip = function(){ this.delayTimeout = null; this.delay = 1500; this.element = document.createElement('div'); this.element.style.display = 'none'; this.element.style.position = 'absolute'; this.element.className = 'tooltip'; document.getElementsByTagName('body')[0].appendChild(this.element); }; Tooltip.prototype = { startDelay:function(e, text){ if(this.delayTimeout == null){ var that = this; var x = e.clientX; var y = e.clientY; this.delayTimeout = setTimeout(function(){ this.show(x, y, text); },this.delay); } }, show:function(x, y, text){ clearTimeout(this.delayTimeout); this.delayTimeout = null; this.element.innerHTML = text; this.element.style.left = x + 'px'; this.element.style.top = y + 'px'; this.element.style.display = 'block'; }, hide:function(){ clearTimeout(this.delayTimeout); this.delayTimeout = null; this.element.style.display = 'none'; } };

return {
addTooltip:function(targetElement, text){
var tt = this.getTooltip();
addEvent(targetElement, 'mouseover', function(e){that.startDelay(e);});
addEvent(targetElement, 'mouseout', function(e){that.hide();});
},
getTooltip:function(){
if(storeInstance == null){
storeInstance = new Tooltip();
return storeInstance;
}
}
};
})();


外在对象被闭包保存。

保存实例供以后重用

var DialogBox = function(){};
DialogBox.prototype = {
show:function(header, body, footer){},
hide:function(){},
state:function(){}
};
DialogBoxManager = (function(){
var created = [];
return {
displayDialogBox:function(header, body, footer){
var inUse = this.numberInUse();
if(inUse > created.length){
created.push(this.createDialogBox());
}
created[inUse].show(header, body, footer);
},
createDialogBox:function(){
var db = new DialogBox();
return db;
},
numberInUse:function(){
var inUse = 0;
for(var i = 0, len = created.length; i < len; i++){
if(created[i].state = 'visible'){
inUse++;
}
}
return inUse;
}
};
})();


这里将创建出的DialogBox保存在created这个闭包变量中,然后获取正在被使用的个数,如果都被使用,就创建,否则就重用。

这样内在对象就就一直被重用,然后外在对象保存在闭包变量中,这里复杂了一下,就相当与数据库的连接池。

享元模式使用的场合:

第一:网页中使用了大规模的密集对象。

第二:这些对象的内部数据能够分离出来,作为参数提供给方法。

第三:分离出的内在的对象比较少,尽量减少独一无二的对象。

享元模式的一般步骤:

1、将所有外在数据从目标类中剥离。尽量删除该类的属性跟构造函数,然后提供给该类的方法。结果是功能一样,数据的来源与方法的参数。

2、创建一个控制该类的实例化工厂,保存创造出的独一无二的对象。一种方法是用一个对象保存引用,然后用参数的唯一指定索引,另一种是对象池,用数组保存引用。

3、创建保存外在数据的管理器。把内在数据提供给工厂对象以创造对象,外在数据保存在管理器的数据结构中。

享元模式之利:

降低网页负荷。

享元模式之弊:

只是在一系列条件下满足的优化,然后创造的对象变少,追踪数据问题困难,维护困难。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: