js设计模式
因为自己刚刚毕业,应公司需求最近在研究js,html,编写了一些网站,见识到了js的强大之处,所以想深入的了解一些这门技术,现在就分享一下自己的js设计模式的一些过程以及感悟,如有不恰当的地方还希望大家可以指正,谢谢!
什么是设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。
设计模式的三条原则
1、适用性-模式怎样才能被认为是成功的。
使用设计模式对我们的开发固然有很大的益处,但是如果找不到合适的设计模式,将会给我们的开发造成不必要的麻烦,所以合适的设计模式才被认为是成功的。
2、有用性-为什么认为这个模式是成功的?
如果我们只是生搬硬套的使用一些设计模式,容易造成对代码的低可控度,在要加入新的需求或者代码重构时会造成很大的麻烦。
3、可用性-因为设计得到广泛的应用,所以认为这个设计就是模式吗?如果是这样的话,那么需要说明。
反模式
如果我们认为模式代表一个最佳的实践,那么反模式将代表我们已经学到一个教训。受启发于Gof的《设计模式》,Andrew Koeing在1995年的11月的C++报告大会上首次提出反模式。在Koeing的报告中,反模式有着两种观念:
描述对于一个特殊的问题,提出了一个糟糕的解决方案,最终导致一个坏结果发生
描述如何摆脱上述解决方案并能提出一个好的解决方案
当我们想要做好一件事情前,最好是先参考前人的做法,尤其是失败的做法,对于js也是一样的,如果我们想要恰当的理解js的设计模式,那么我们必须要了解js的一些反模式。
反模式是一个值得为此专门编写编写总结文档的糟糕设计。Javascript的反模式例子如下:
在全局中定义大量污染全局命令空间的变量
在调用setTimeout和setInterval时传递字符串(会用eval来执行)而不是函数。
修改Object的原型 (这是最糟糕的反模式)
使用内联Javascript
在本应使用document.createElement的地方使用document.write。
document.write被错误的用了相当多的年头,它有相当多的缺点,包括如果在页面加载后执行它可能会覆盖我们的页面。再有它不能工作在XHTML下,这也是另外一个我们使用像document.createElement这种对DOM友好方法的原因。
知道反模式对成功来说很关键。一旦我们能识别这些反模式,我们就能够重构我们的代码使项目的整体质量立马提升。
js设计模式的分类
创建型设计模式:构造器模式(Constructor),工厂模式(Factory),抽象工厂模式 (Abstract),原型模式 (Prototype),单例模式 (Singleton)以及 建造者模式(Builder)
结构设计模式:装饰模式,外观模式,享元模式,适配器模式和代理模式。
行为设计模式:迭代模式,中介者模式,观察者模式和访问者模式。
js中的类
因为js是一门弱类型语言,所以类的概念不是那么的明显,但是也是可以模拟的。
例如:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>test</title> </head> <body> <div id="show" style="width:400px;height:400px;margin: auto auto;background-color: #EE7700"></div> <script> //模拟class function class_a(animalname){ this.animalname=animalname; this.color="黑色"; this.weight=100; this.infor=function(){ return "它是一只"+this.weight+"斤的"+this.color+"的"+this.animalname; } } var animal=new class_a("小狗"); animal.color="白色"; animal.weight=20; document.getElementById("show").innerHTML=animal.infor(); </script> </body> </html>
言归正传,开始模式之旅
构造器模式
在面向对象编程中,构造器是一个当新建对象的内存被分配后,用来初始化该对象的一个特殊函数。在Javascript中几乎所有的东西都是对象,我们经常会对对象的构造器十分感兴趣。
对象构造器是被用来创建特殊类型的对象的,首先它要准备使用的对象,其次在对象初次被创建时,通过接收参数,构造器要用来对成员的属性和方法进行赋值。
1、基础构造器
在面向对象中,构造器是一个在新建对象的内存被分配之后,用来初始化这个对象的函数,在js中几乎所有的东西都是对象,所以构造器模式的使用在js中是十分常见的,所以直接上例子,不做赘述。
使用prototype。
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text/javascript" language="JavaScript" src="./js/jquery.min.js"></script> <script type="text/javascript" language="JavaScript" src="./js/jquery.tinyscrollbar.min.js"></script> </head> <body> <div id="output" style="width: 200px;height: 200px;margin: auto auto;background-color: chocolate"></div> <script> var animal=function(years,weight,color){ this.years=years; this.weight=weight; this.color=color; } animal.prototype.show=function(){ $("#output").html("颜色:"+this.color+"身高:"+this.weight+"年龄:"+this.years); } var a1=new animal(2,20,"blue"); a1.show(); </script> </body> </html>
2、模块化模式:
模块是任何健壮的应用程序体系结构不可或缺的一部分,特点是有助于保持应用项目的代码单元既能清晰地分离又有组织。
模块化模式最初被定义为一种对传统软件工程中的类提供私有和公共封装的方法。在JavaScript中,模块化模式用来进一步模拟类的概念,通过这样一种方式:我们可以在一个单一的对象中包含公共/私有的方法和变量,从而从全局范围中屏蔽特定的部分。这个结果是可以减少我们的函数名称与在页面中其他脚本区域定义的函数名称冲突的可能性。
这里是我自己的理解,模块化模式帮助我们更好地模拟类似java和c++的模式,可以定义一个方法,模仿在方法中定义私有和公共变量,对外界屏蔽特定的方法或者变量,仅为外界提供相应的接口,使用户不再关心内部的东西,也使我们的程序更加的有序,例如不同的功能对应不同的方法等。下面上例子:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text/javascript" language="JavaScript" src="./js/jquery.min.js"></script> <script type="text/javascript" language="JavaScript" src="./js/jquery.tinyscrollbar.min.js"></script> </head> <body> <div id="output" style="width: 200px;height: 200px;margin: auto auto;background-color: chocolate"></div> <script> //让我们来人造人 var people=(function(){ //私有的,外界不可访问 var sex="男"; var old=0; return{ builder:"rui", init:function(){ return "初始化了一个"+old+"岁的"+sex+"孩"; } } })(); var first=people.init(); $("#output").html(first); </script> </body> </html>
优势:
既然我们已经看到单例模式很有用,为什么还是使用模块模式呢?首先,对于有面向对象背景的开发者来讲,至少从javascript语言上来讲,模块模式相对于真正的封装概念更清晰。
其次,模块模式支持私有数据-因此,在模块模式中,公共部分代码可以访问私有数据,但是在模块外部,不能访问类的私有部分
劣势:
模块模式的缺点是因为我们采用不同的方式访问公有和私有成员,因此当我们想要改变这些成员的可见性的时候,我们不得不在所有使用这些成员的地方修改代码。
我们也不能在对象之后添加的方法里面访问这些私有变量。也就是说,很多情况下,模块模式很有用,并且当使用正确的时候,潜在地可以改善我们代码的结构。
其它缺点包括不能为私有成员创建自动化的单元测试,以及在紧急修复bug时所带来的额外的复杂性。根本没有可能可以对私有成员打补丁。相反地,我们必须覆盖所有的使用存在bug私有成员的公共方法。开发者不能简单的扩展私有成员,因此我们需要记得,私有成员并非它们表面上看上去那么具有扩展性。
改进版本:暴露式模块模式
<script> //让我们来人造人 var people=(function(jQ){ var sex="男"; var old=0; var init=function(){ jQ("#output").html("初始化了一个"+old+"岁的"+sex+"孩"); } return{ builder:"rui", getpeople:init } })(jQuery); people.getpeople(); </script>
3、单例模式
之所以叫做单例模式是因为它限定对于一个类,它他只允许有一个实例化对象,经典的实现方式是,创建一个类,这个类包含一个方法,这个方法在没有对象存在的情况下,将会创建一个新的实例对象。如果对象存在,这个方法只是返回这个对象的引用。
单例和静态类不同,因为我们可以退出单例的初始化时间。通常这样做是因为,在初始化的时候需要一些额外的信息,而这些信息在声明的时候无法得知。对于并不知晓对单例模式引用的代码来讲,单例模式没有为它们提供一种方式可以简单的获取单例模式。这是因为,单例模式既不返回对象也不返回类,它只返回一种结构。
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text/javascript" language="JavaScript" src="./js/jquery.min.js"></script> <script type="text/javascript" language="JavaScript" src="./js/jquery.tinyscrollbar.min.js"></script> </head> <body> <div id="output" style="width: 200px;height: 200px;margin: auto auto;background-color: chocolate"></div> <script> var single=( function(){ var instance; function init(){ function privatemethod(){ $("#output").append("<p>这是一个私有方法:privatemethod()</p>"); } var privatevair="我是一个私有变量"; var privateRandomNumber=Math.random(); return{ publicMethod:function(){ $("#output").append("<p>这是一个公共的方法:publicMethod()</p>"); }, publicvair:"我是一个公共的变量1", getRandomNumber: function () { return privateRandomNumber; } }; }; return{ aaa:0, getInstance: function () { if(!instance){ instance=init(); } return instance; } }; })(); var singleA=single.getInstance();//调用 // 这里single.aaa为0,但是singleA.aaa为undefine未定义 if(single.aaa==singleA.aaa) alert("sdas"); // singleA.privatemethod();//无法访问 // $("#output").append("<p>"+singleA.privatevair+"</p>");//这是无法访问的 singleA.publicMethod(); $("#output").append("<p>"+singleA.publicvair+"</p>"); </script> </body> </html>
在四人帮(GoF)的书里面,单例模式的应用描述如下:
每个类只有一个实例,这个实例必须通过一个广为人知的接口,来被客户访问。
子类如果要扩展这个唯一的实例,客户可以不用修改代码就能使用这个扩展后的实例。
区分类的静态实例和单例模式很重要:尽管单例模式可以被实现成一个静态实例,但是单例可以懒构造,在真正用到之前,单例模式不需要分配资源或者内存。
如果我们有个静态对象可以被直接初始化,我们需要保证代码总是以同样的顺序执行(例如 汽车需要轮胎先初始化)当你有很多源文件的时候,这种方式没有可扩展性。
4、观察者模式
对于这个模式的定义我自身比较喜欢这个定义,来自于四人帮的《设计模式:可重用的面向对象软件的元素》:
"一个或者更多的观察者对一个被观察者的状态感兴趣,将自身的这种兴趣通过附着自身的方式注册在被观察者身上。当被观察者发生变化,而这种便可也是观察者所关心的,就会产生一个通知,这个通知将会被送出去,最后将会调用每个观察者的更新方法。当观察者不在对被观察者的状态感兴趣的时候,它们只需要简单的将自身剥离即可。“
需要说明的是被观察者通知观察者的方式是广播,这条广播内可能含有特定于这条通知的数据信息。
我们现在可以通过实现一个观察者模式来进一步扩展我们刚才所学到的东西。这个实现包含一下组件:
被观察者:维护一组观察者, 提供用于增加和移除观察者的方法。
观察者:提供一个更新接口,用于当被观察者状态变化时,得到通知。
具体的被观察者:状态变化时广播通知给观察者,保持具体的观察者的信息。
具体的观察者:保持一个指向具体被观察者的引用,实现一个更新接口,用于观察,以便保证自身状态总是和被观察者状态一致的。
如:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script> function ObserverList(){ this.observerList = []; } ObserverList.prototype.Add = function( obj ){ return this.observerList.push( obj ); }; ObserverList.prototype.Empty = function(){ this.observerList = []; }; ObserverList.prototype.Count = function(){ return this.observerList.length; }; ObserverList.prototype.Get = function( index ){ if( index > -1 && index < this.observerList.length ){ return this.observerList[ index ]; } }; ObserverList.prototype.Insert = function( obj, index ){ var pointer = -1; if( index === 0 ){ this.observerList.unshift( obj ); pointer = index; }else if( index === this.observerList.length ){ this.observerList.push( obj ); pointer = index; } return pointer; }; ObserverList.prototype.IndexOf = function( obj, startIndex ){ var i = startIndex, pointer = -1; while( i < this.observerList.length ){ if( this.observerList[i] === obj ){ pointer = i; } i++; } return pointer; }; ObserverList.prototype.RemoveAt = function( index ){ if( index === 0 ){ this.observerList.shift(); }else if( index === this.observerList.length -1 ){ this.observerList.pop(); } }; // Extend an object with an extension function extend( extension, obj ){ for ( var key in extension ){ obj[key] = extension[key]; } } function Subject(){ this.observers = new ObserverList(); } Subject.prototype.AddObserver = function( observer ){ this.observers.Add( observer ); }; Subject.prototype.RemoveObserver = function( observer ){ this.observers.RemoveAt( this.observers.IndexOf( observer, 0 ) ); }; Subject.prototype.Notify = function( context ){ var observerCount = this.observers.Count(); for(var i=0; i < observerCount; i++){ this.observers.Get(i).Update( context ); } }; // The Observer function Observer(){ this.Update = function(){ // ... }; } </script> </head> <body> <button id="addNewObserver">Add New Observer checkbox</button> <input id="mainCheckbox" type="checkbox"/> <div id="observersContainer"></div> <script> var controlCheckbox = document.getElementById( "mainCheckbox" ), addBtn = document.getElementById( "addNewObserver" ), container = document.getElementById( "observersContainer" ); // 具体的被观察者 //Subject 类扩展controlCheckbox 类 extend( new Subject(), controlCheckbox ); //点击checkbox 将会触发对观察者的通知 controlCheckbox["onclick"] = new Function( "controlCheckbox.Notify(controlCheckbox.checked)" ); addBtn["onclick"] = AddNewObserver; // 具体的观察者 function AddNewObserver(){ //建立一个新的用于增加的checkbox var check = document.createElement( "input" ); check.type = "checkbox"; // 使用Observer 类扩展checkbox extend( new Observer(), check ); // 使用定制的Update函数重载 check.Update = function( value ){ this.checked = value; }; // 增加新的观察者到我们主要的被观察者的观察者列表中 controlCheckbox.AddObserver( check ); // 将元素添加到容器的最后 container.appendChild( check ); } </script> </body> </html>
观察者模式鼓励人们认真考虑应用不同部分之间的关系,同时帮助我们找出这样的层,该层中包含有直接的关系,这些关系可以通过一些列的观察者和被观察者来替换掉。这中方式可以有效地将一个应用程序切割成小块,这些小块耦合度低,从而改善代码的管理,以及用于潜在的代码复用。
使用观察者模式更深层次的动机是,当我们需要维护相关对象的一致性的时候,我们可以避免对象之间的紧密耦合。例如,一个对象可以通知另外一个对象,而不需要知道这个对象的信息。
两种模式下,观察者和被观察者之间都可以存在动态关系。这提供很好的灵活性,而当我们的应用中不同的部分之间紧密耦合的时候,是很难实现这种灵活性的。
尽管这些模式并不是万能的灵丹妙药,这些模式仍然是作为最好的设计松耦合系统的工具之一,因此在任何的JavaScript 开发者的工具箱里面,都应该有这样一个重要的工具。
5、中介者模式
中介者是一个行为设计模式,使我们可以导出统一的接口,这样系统不同部分就可以彼此通信。如果系统组件之间存在大量的直接关系,就可能是时候,使用一个中心的控制点,来让不同的组件通过它来通信。中介者通过将组件之间显式的直接的引用替换成通过中心点来交互的方式,来做到松耦合。这样可以帮助我们解耦,和改善组件的重用性。
在现实世界中,类似的系统就是,飞行控制系统。一个航站塔(中介者)处理哪个飞机可以起飞,哪个可以着陆,因为所有的通信(监听的通知或者广播的通知)都是飞机和控制塔之间进行的,而不是飞机和飞机之间进行的。一个中央集权的控制中心是这个系统成功的关键,也正是中介者在软件设计领域中所扮演的角色。
简单的实现:你可以看到setsex以及setold已经被暴露出来使用
<script> //让我们来人造人 var people=(function(){ //私有的,外界不可访问 var sex; var old; var setsex= function () { sex="男"; } var setold=function(){ old=0; } return{ setsex:setsex, setold:setold, installTo: function( obj ){ obj.setsex = setsex; obj.setold = setold; } } })(); </script>
中间人模式最大的好处就是,它节约了对象或者组件之间的通信信道,这些对象或者组件存在于从多对多到多对一的系统之中。由于解耦合水平的因素,添加新的发布或者订阅者是相对容易的。
也许使用这个模式最大的缺点是它可以引入一个单点故障。在模块之间放置一个中间人也可能会造成性能损失,因为它们经常是间接地的进行通信的。由于松耦合的特性,仅仅盯着广播很难去确认系统是如何做出反应的。
这就是说,提醒我们自己解耦合的系统拥有许多其它的好处,是很有用的——如果我们的模块互相之间直接的进行通信,对于模块的改变(例如:另一个模块抛出了异常)可以很容易的对我们系统的其它部分产生多米诺连锁效应。这个问题在解耦合的系统中很少需要被考虑到。
在一天结束的时候,紧耦合会导致各种头痛,这仅仅只是另外一种可选的解决方案,但是如果得到正确实现的话也能够工作得很好。
6、工厂模式
工厂模式其实就是另外一种对象创建的模式,但是这种模式并不明确的规定使用构造器,取而代之的是工厂的概念,一个工厂提供一个创建对象的公共接口,我们可以在其中指定我们想要创建对象的类型。
例如:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text/javascript" language="JavaScript" src="./js/jquery.min.js"></script> <script type="text/javascript" language="JavaScript" src="./js/jquery.tinyscrollbar.min.js"></script> </head> <body> <div id="output" style="width: 200px;height: 200px;margin: auto auto;background-color: chocolate"></div> <script> function Car(options){ this.color=options.color|| "red"; this.band=options.band || "benchi"; this.speed=options.speed || 120; } function airplane(options){ this.color=options.color || "black"; this.band=options.band || "China"; this.speed=options.speed || 5000; } function FactoryModle(){}; FactoryModle.prototype.vehicleClass=Car; FactoryModle.prototype.CreateVehicle=function(options){ if(options.vehicletype=="car") { this.vehicleClass=Car; } else { this.vehicleClass=airplane; } return new this.vehicleClass(options); } var first=new FactoryModle(); var car=first.CreateVehicle({ vehicletype:"car", color:"blue", band:"lanbojini", speed:150 }); $("#output").html(car.band+" "+car.color+" "+car.speed); </script> </body> </html>
何时使用:
当被应用到下面的场景中时,工厂模式特别有用:
当我们的对象或者组件设置涉及到高程度级别的复杂度时.
当我们需要根据我们所在的环境方便的生成不同对象的实体时.
当我们在许多共享同一个属性的许多小型对象或组件上工作时.
当带有其它仅仅需要满足一种API约定(又名鸭式类型)的对象的组合对象工作时.这对于解耦来说是有用的.
何时不适合使用:
当被应用到错误的问题类型上时,这一模式会给应用程序引入大量不必要的复杂性.除非为创建对象提供一个接口是我们编写的库或者框架的一个设计上目标,否则我会建议使用明确的构造器,以避免不必要的开销.
由于对象的创建过程被高效的抽象在一个接口后面的事实,这也会给依赖于这个过程可能会有多复杂的单元测试带来问题.
抽象工厂模式:
前面我们知道了什么叫做工厂模式,这里要讲的抽象工厂模式就是工厂模式的集合,它以一个通用的目标将一组独立的工厂进行封装,它将一堆对象的实现细节从他们的一般用例中分离。
例如:
<script> function Car(options){ this.color=options.color|| "red"; this.band=options.band || "benchi"; this.speed=options.speed || 120; } function airplane(options){ this.color=options.color || "black"; this.band=options.band || "China"; this.speed=options.speed || 5000; } // function FactoryModle(){}; // //默认的指像Car类 // FactoryModle.prototype.vehicleClass=Car; // FactoryModle.prototype.CreateVehicle=function(options){ // if(options.vehicletype=="car") // { // this.vehicleClass=Car; // } // else // { // this.vehicleClass=airplane; // } // return new this.vehicleClass(options); // } // var first=new FactoryModle(); // var car=first.CreateVehicle({ // vehicletype:"", // color:"blue", // band:"lanbojini", // speed:150 // }); // $("#output").html(car.band+" "+car.color+" "+car.speed); var AbstractVehicleFactory = (function () { // Storage for our vehicle types var types = {}; return { getVehicle: function ( type, customizations ) { var Vehicle = types[type]; return (Vehicle ? new Vehicle(customizations) : null); }, registerVehicle: function ( type, Vehicle ) { var proto = Vehicle.prototype; // only register classes that fulfill the vehicle contract if ( proto.color && proto.band ) { types[type] = Vehicle; } return AbstractVehicleFactory; } }; })(); // Usage: AbstractVehicleFactory.registerVehicle( "car", Car ); AbstractVehicleFactory.registerVehicle( "airplane", airplane ); // Instantiate a new car based on the abstract vehicle type var car = AbstractVehicleFactory.getVehicle( "car" , { color: "lime green", band: "baoma" } ); // Instantiate a new truck in a similar manner var truck = AbstractVehicleFactory.getVehicle( "airplane" , { band: "dazhong", color: "neon yellow" } ); </script>
7、织入模式
织入模式就是提供一些提供一个或者一组子类进行简单继承功能的类,主要目的是重用其功能。
此处不做细究。
8、装饰器模式
装饰器模式是为了提高代码重用性的一种编码方式。它不关心对象创建的过程,而是专注于扩展他们的功能这个问题上。
典型的装饰器提供了向一个系统中现有的类动态添加行为的能力。其创意是装饰本身并不关心类的基础功能,而只是将它自身拷贝到超类之中。
例如:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text/javascript" language="JavaScript" src="./js/jquery.min.js"></script> <script type="text/javascript" language="JavaScript" src="./js/jquery.tinyscrollbar.min.js"></script> </head> <body> <div id="output" style="width: 200px;height: 200px;margin: auto auto;background-color: chocolate"></div> <script> function Vehicle(vehicletype){ this.vehicletype=vehicletype||"car"; this.color="red"; this.band="China"; } var car=new Vehicle("car"); var airplane=new Vehicle("airplane"); airplane.setspeed= function (speed) { this.speed=speed; } airplane.show=function(){ $("#output").append("<p>"+airplane.vehicletype+" "+airplane.speed+"</p>"); } airplane.setspeed(200); airplane.show(); </script> </body> </html>
这个只是简单地实现了装饰器模式,虽然十分的简单但是是很方便实用的,在我们的日常编程中使用也是十分的频繁。
下面看个复杂的:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text/javascript" language="JavaScript" src="./js/jquery.min.js"></script> <script type="text/javascript" language="JavaScript" src="./js/jquery.tinyscrollbar.min.js"></script> </head> <body> <div id="output" style="width: 200px;height: 200px;margin: auto auto;background-color: chocolate"></div> <script> function MacBook() { this.cost = function () { return 925; }; this.screenSize = function () { return 11.6; }; } // Decorator 1 function Memory( macbook ) { var v = macbook.cost(); macbook.cost = function() { return v + 75; }; } // Decorator 2 function Engraving( macbook ){ var v = macbook.cost(); macbook.cost = function(){ return v + 200; }; } // Decorator 3 function Insurance( macbook ){ var v = macbook.cost(); macbook.cost = function(){ return v + 250; }; } var mb = new MacBook(); Memory( mb ); Engraving( mb ); Insurance( mb ); </script> </body> </html>
在上面的示例中,我们的装饰器重载了超类对象MacBook()的 object.cost()函数,使其返回的Macbook的当前价格加上了被定制后升级的价格。
这被看做是对原来的Macbook对象构造器方法的装饰,它并没有将其重写(例如,screenSize()),我们所定义的Macbook的其它属性也保持不变,完好无缺。
上面的示例并没有真正定义什么接口,而且我们也转移了从创造者到接受者移动时确保一个对象对应一个接口的责任。
优点和缺点:
因为它可以被透明的使用,并且也相当的灵活,因此开发者都挺乐意去使用这个模式——如我们所见,对象可以用新的行为封装或者“装饰”起来,而后继续使用,并不用去担心基础的对象被改变。在一个更加广泛的范围内,这一模式也避免了我们去依赖大量子类来实现同样的效果。
然而在实现这个模式时,也存在我们应该意识到的缺点。如果穷于管理,它也会由于引入了许多微小但是相似的对象到我们的命名空间中,从而显著的使得我们的应用程序架构变得复杂起来。这里所担忧的是,除了渐渐变得难于管理,其他不能熟练使用这个模式的开发者也可能会有一段要掌握它被使用的理由的艰难时期。
足够的注释或者对模式的研究,对此应该有助益,而只要我们对在我们的应程序中的多大范围内使用这一模式有所掌控的话,我们就能让两方面都得到改善。
9、享元模式
享元模式是一种优化重复,缓慢,低效率代码的经典模式,它是通过以相关对象尽可能多的共享数据的方式来降低程序内存的使用,其实就是一句话:复用我们内存中已存在的对象,降低系统创建对象实例的性能消耗。
在这个实现中我们将要使用如下所列的三种类型的享元组件:
享元对应的是一个接口,通过此接口能够接受和控制外在状态。
构造享元来实际的实际的实现接口,并存储内在状态。构造享元须是能够被共享的,并且具有操作外在状态的能力。
享元工厂负责管理享元对象,并且也创建它们。它确保了我们的享元对象是共享的,并且可以对其作为一组对象进行管理,这一组对象可以在我们需要的时候查询其中的单个实体。如果一个对象已经在一个组里面创建好了,那它就会返回该对象,否则它会在对象池中新创建一个,并且返回之。
经过了两天的时间,因为是初次学习设计模式,所以自己的理解还是很浅显的,希望大家有什么意见或者建议都可以提出来,其中有一些东西是借鉴整理别人的理解,可能也有很多不正确或者不恰当的东西,大家可以一起来探讨,谢谢!