1、JavaScript实现接口
方法1:注释
//interface Composite{ // function add(obj); // function remove(obj); // function update(obj); //} //CompositeImpl必须实现add remove update 3个方法 function CompositeImpl(){} CompositeImpl.prototype.add=function(obj){}; CompositeImpl.prototype.remove=function(obj){}; CompositeImpl.prototype.update=function(obj){}; var c1 = new CompositeImpl(); var c2 = new CompositeImpl();
方法2:属性检测
function CompositeImpl(){ //显式的在类的内部接受所实现的接口 //一般来说在类的内部要定义一个变量,接收实现接口的名字 this.implementsInterface = ["Composite","FormItem"]; } CompositeImpl.prototype.add=function(obj){}; CompositeImpl.prototype.remove=function(obj){}; CompositeImpl.prototype.update=function(obj){}; CompositeImpl.prototype.select=function(obj){}; //检测函数 function CheckCompositeImpl(instance){ //判断当前对象是否实现了所有的接口 if(!IsImplements(instance,"Composite","FormItem")){ throw new Error("没有实现所有的方法"); } } //公用的具体的检测方法 核心方法 function IsImplements(obj){ for(var i=1;i<arguments.length;i++){ var interfaceName = arguments[i]; var interfaceFound = false; for(var j=0;j<obj.implementsInterface.length;j++){ if(obj.implementsInterface[j] == interfaceName){ interfaceFound=true; break; } } if(!interfaceFound){ return false; } } return true; } var c1=new CompositeImpl(); CheckCompositeImpl(c1);
方法3:鸭式辩型
类实现接口的目的就是要把接口里面所有的方法都实现
鸭式辩型核心的核心就是检测方法
//1、接口类 用来实例化接口 //需要两个参数 1、接口名字 2、方法名数组 function Interface(name,methods){ if(arguments.length != 2){ throw new Error("该接口实例化对象构造函数参数不是2"); } this.name = name; this.method = []; for(var i=0,len=methods.length;i<len;i++){ if(typeof methods[i] !== “string”){ throw new Error("接口方法名不是string类型"); } this.methods.push(methods[i]); } } Interface.ensureImplements = function(obj){ if(arguments.length < 2){ throw new Error("ensureImplements参数个数为"+arguments.length+",不符合要求"); } for(var i=1;i<arguments.length;i++){ var instanceInterface = arguments[i]; if(instanceInterface.constructor !== Interface){ throw new Error("参数类型不是Interface接口类型"); } for(var j=0;j<instanceInterface.methods.length;j++){ var methodName=instanceInterface.methods[j]; if(!obj[methodName] || typeof object[methodName]!="function"){ throw new Error(methodName+"方法没有实现或者不是函数类型"); } } } }; function CompositeImpl(){ } CompositeImpl.prototype.add=function(obj){}; CompositeImpl.prototype.remove=function(obj){}; CompositeImpl.prototype.update=function(obj){}; CompositeImpl.prototype.select=function(obj){}; var CompositeInterface = new Interface("CompositeInterface",["add","remove"]); var FormItemInerface = new Interface("FormItemInerface",["update","select"]); var c1 = new CompositeImpl(); Interface.ensureImplements(c1,CompositeInterface,FormItemInerface);
2、单体模式
//简单单体 var Singleton={ attr1:true, attr2:10, method1:function(){ alert("方法一"); }, method2:function(){ alert("方法二"); } }; alert(Singleton.attr1);
//借助闭包创建单体 闭包的主要目的是保护数据 拥有自己的作用域 var BHX={}; BHX.Singleton=(function(){ //在这个作用域里面可以添加自己的成员 var a1=true; var a2=10; function f1(){ alert("f1"); } function f2(){ alert("f2"); } return { attr1:a1, attr2:a2, method1:f1, method2:f2 }; })();
alert(BHX.Singleton.attr1);
//惰性单体 和闭包单体有一些相似的地方 var Ext={}; Ext.Base=(function(){ var uniqInstance; function init(){ var a1=10; var a2=true; var fn1=function(){alert("fn1");}; var fn2=function(){alert("fn2");}; return { attr1:a1, attr2:a2, method1:fn1, method2:fn2 }; } return { getInstance:function(){ if(!uniqInstance){ uniqInstance=init(); } return uniqInstance; }, }; })(); alert(Ext.Base.getInstance().attr1);
//分支单体 判断程序的分支 和switch类似 var Ext={}; var diff=true; Ext.More=(function(){ var objA={ attr1:"FF" }; var objB={ attr1:"IE" }; return def ? objA : objB; })(); alert(Ext.More.attr1);
3、函数链式调用
function Dog(){ this.run=function(){ alert("Dog is running"); return this; }; this.eat=function(){ alert("Dog is eating"); return this; }; this.sleep=function(){ alert("Dog is sleeping"); return this; }; } var d1=new Dog(); d1.run(); d1.sleep(); d1.eat();
//==================jquery模拟 (function(){ function _$(arguments){ var idSel=/^#w+/; this.dom; if(idSel.test(arguments[0])){ this.dom=document.getElementById(arguments[0].substring(1)); }else{ throw new Error("无法匹配id"); } } Function.prototype.method=function(methodName,fn){ this.prototype[methodName]=fn; return this; }; _$.prototype={ constructor:_$, addEvent:function(){}, setStyle:function(){} }; _$.onReady=function(fn){ window.$=function(){ return new _$(arguments); }; fn(); _$.method("addEvent",function(){ return this; }).method("setStyle",function(){ return this; }); }; window.$=_$; })(window); $.onReady(function(){ $("#inp") });
4、工厂方式
function CarShop(){ } CarShop.prototype={ constructor:CarShop, sellCar:function(type){ var car=CarFactory.createCar(type); return car; //这是生产车的逻辑,所以不应该出现在这里 //var car; //switch(type){ // case "Benz": // car=new Benz(); // break; // case "Bmw": // car=new Bmw(); // break; // case "Audi": // car=new Audi(); // break; // default: // "没有合适的车"; //} //Interface.ensureImplements(car,CarInterface); //return car; }, }; var CarInterface = new Interface("CarInterface",["start","run"]); function BaseCar(){} BaseCar.prototype={ constructor:BaseCar, start:function(){}, run:function(){} }; function Benz(){} extend(Benz,BaseCar); function Bmw(){} extend(Bmw,BaseCar); function Audi(){} extend(Audi,BaseCar); var shop=new CarShop(); var car=shop.sellCar("Benz"); car.start(); car.run(); //注:在CarShop的sellCar方法中,既有卖车的逻辑,又有生产车的逻辑(new Benz() new Bmw()...) //生产车的逻辑应该交给工厂来做 //生产一台车,单体模式 var CarFactory={ createCar:function(){ var car; switch(type){ case "Benz": car=new Benz(); break; case "Bmw": car=new Bmw(); break; case "Audi": car=new Audi(); break; default: "没有合适的车"; } Interface.ensureImplements(car,CarInterface); return car; } };
==================复杂工厂(真正的工厂)================ //买什么车去什么商店->商店->卖车->生产车->工厂 //需要一个商店抽象类,将CarShop作为抽象类 function CarShop(){} CarShop.prototype={ constructor:CarShop, sellCar:function(type){ //var car=CarFactory.createCar(type); //return car; this.abstractSellCar(type); }, abstractSellCar:function(){ throw new Error("抽象类不可以直接调用"); } }; function BenzCarShop(){} extend(BenzCarShop,CarShop); BenzCarShop.prototype={ constructor:BenzCarShop, sellCar:function(type){ var car; var types=["Benz"]; for(var t in types){ if(types[t]===type){ car=CarFactory.createCar(type); }else{ alert("没有对应的车型号"); } } return car; //var car=CarFactory.createCar(type); //return car; } }; function BmwCarShop(){} extend(BmwCarShop,CarShop); BmwCarShop.prototype={ constructor:BmwCarShop, sellCar:function(type){ var car; var types=["Bmw"]; for(var t in types){ if(types[t]===type){ car=CarFactory.createCar(type); }else{ alert("没有对应的车型号"); } } return car; //var car=CarFactory.createCar(type); //return car; } }; var CarInterface = new Interface("CarInterface",["start","run"]); function BaseCar(){} BaseCar.prototype={ constructor:BaseCar, start:function(){}, run:function(){} }; function Benz(){} extend(Benz,BaseCar); function Bmw(){} extend(Bmw,BaseCar); function Audi(){} extend(Audi,BaseCar); var CarFactory={ createCar:function(){ //利用eval动态创建传入类型的实例对象 var car=eval("new "+type+"()"); Interface.ensureImplements(car,CarInterface); return car; } }; var shop1=new BenzCarShop(); var car1=shop1.sellCar(); car1.run();
5、桥接模式
桥接模式 事件绑定 window.onload=function(){ var oInput=document.getElementById("ipt"); oInput.addEventListener("click",sendReq,false); function sendReq(){ $.post("URL",{msg:"abc"},function(result){ }); } }; 特权函数 使内部和外部解耦 function Public(){ var name="张三"; this.getName=function(){ return name; }; } var p1=new Public(); alert(p1.getName()); =============== function Public(){ var privateMethod=function(){ alert("复杂操作"); }; this.bridgeMethod=function(){ return privateMethod(); }; } var p1=new Public(); p1.bridgeMethod(); ============== 用桥把多个单体组织在一起 使每个单元都能独立化,可以实现自己的变化 function Class1(a,b,c){ this.a=a; this.b=b; this.c=c; } function Class2(d,e){ this.d=d; this.e=e; } function BridgeClass(a,b,c,d,e){ this.class1=new Class1(a,b,c); this.class2=new Class2(d,e); }
6、组合模式
专门为Web上的动态用户界面而量身定制的
这种模式可以用一条命令在多个对象上激发复杂的或递归的行为
好处:用同样的方法处理对象的集合与其中的特定子对象
还可以用来把一批子对象组织成树状结构,并且使整棵树都可以被遍历
function Org(name){ this.name=name; this.depts=[]; } Org.prototype={ constructor:Org, addDepts:function(child){ this.depts.push(child); return this; }, getDepts:function(){ return this.depts; } }; function Dept(name){ this.name=name; this.persons=[]; } Dept.prototype={ constructor:Dept, addPersons:function(child){ this.persons.push(child); return this; }, getPersons:function(){ return this.persons; } }; function Person(name){ this.name=name; } Person.prototype={ constructor:Person, hardworking:function(){ alert("working"); }, sleeping:function(){ alert("sleeping"); } }; var p1=new Person("张1"); var p2=new Person("张2"); var p3=new Person("张3"); var p4=new Person("张4"); var p5=new Person("张5"); var p6=new Person("张6"); var dept1=new Dept("开发部"); dept1.addPersons(p1).addPersons(p2).addPersons(p3); var dept2=new Dept("销售部"); dept2.addPersons(p4).addPersons(p5).addPersons(p6); var org=new Org("bjsxt"); org.addDepts(dept1).addDepts(dept2); //需求:具体让某个人工作 for(var i=0,depts=org.getDepts();i<depts.length;i++){ var dept=depts[i]; for(var j=0;j<dept.getPersons().length;j++){ if(dept.getPersons()[j].name==="张3"){ dept.getPersons()[j].hardworking(); } } } //当需求更改为开发部下又分为开发部1 开发部2 开发部3 //再让某个子开发部的某个人去工作,那么for循环就会再嵌套一层,if判断也更加复杂 //业务逻辑的更改造成了代码的大幅度变化 //此时我们可以让组合模式来帮我们 //组合模式应用的场景和特点 //场景: //1、存在一批组织成某种层次体系的对象 //2、希望对这批对象或其中一部分对象实施一个操作 //特点: //组合模式中只有两种类型对象:组合对象、叶子对象 //这两种类型都实现同一批接口 //一般我们会在组合对象中调用其方法并隐式调用下级方法,一般会采用递归的形式实现 //组合对象 function Composite(name){ this.name=name; this.type="Composite";//说明对象的类型 this.children=[]; } Composite.prototype={ constructor:Composite, addChild:function(child){ this.children.push(child); return this; }, getChild:function(name){ var elements=[]; var pushLeaf=function(item){ if(item.type === "Composite"){ item.children.each(arguments.callee); }else if(item.type === "Leaf"){ elements.push(item); }else{ throw new Error("类型错误"); } }; if(name && this.name !== name){//根据name让指定name下的Leaf对象执行操作 this.children.each(function(item){ if(item.name === name && item.type === "Composite"){ item.children.each(pushLeaf); }else if(item.name !=== name && item.type === "Composite"){ item.children.each(arguments.callee); }else if(item.name === name && item.type === "Leaf"){ elements.push(item); } }); }else{//让所有的Leaf结点执行操作 //this.children得到的时开发部和销售部 this.children.each(pushLeaf); } return elements; }, hardworking:function(name){ //得到所有的Leaf类型的对象 var leafObject=this.getChild(name); for(var i=0;i<leafObject.length;i++){ leafObject[i].hardworking(); } }, sleeping:function(name){ var leafObject=this.getChild(name); for(var i=0;i<leafObject.length;i++){ leafObject[i].sleeping(); } } }; //叶子对象 function Leaf(name){ this.name=name; this.type="leaf"; } Leaf.prototype={ constructor:Leaf, addChild:function(child){ throw new Error("叶子结点不能添加子对象"); }, getChild:function(name){ if(this.name==name){ return this; } return null; }, hardworking:function(){ alert(this.name+" working"); }, sleeping:function(){ alert(this.name+" sleeping"); } }; var CompositeInterface=new Interface("CompositeInterface",["getChild","addChild"]); var LeafInterface=new Interface("LeafInterface",["hardworking","sleeping"]); var p1=new Leaf("张1"); var p2=new Leaf("张2"); var p3=new Leaf("张3"); var p4=new Leaf("张4"); var p5=new Leaf("张5"); var p6=new Leaf("张6"); var dept1=new Composite("开发部"); dept1.addChild(p1).addChild(p2).addChild(p3); var dept2=new Composite("销售部"); dept2.addChild(p4).addChild(p5).addChild(p6); var org=new Composite("bjsxt"); org.addChild(dept1).addChild(dept2); org.hardworking("开发部"); //如果不传参数,就让所有结点调用hardworking方法 org.hardworking();
7、门面模式
//门面模式的两个作用:简化类的接口,消除类和使用它的客户代码之间的耦合 //是最常用的设计模式 //最经典的就是事件 //做一件事情 必须调用2个函数 分别是a b //其实就是不要把所有的逻辑都写在一个函数里面,要分开写 function a(x){} function b(y){} function ab(x,y){ a(x); b(y); }
8、适配器模式
//现有的接口和不兼容的类之间进行适配。使用这种模式的对象又叫包装器,因为他们是在用一个新的接口包装另一个对象。借助适配器可以处理一些与API不匹配、不能一同使用的情况 var obj={ str1:"111", str1:"222", str1:"333" }; function adapter(obj){ interfaceMethod(obj.str1,obj.str2,obj.str3); } function interfaceMethod(x,y,z){ } =================== //假设要兼容YUI和prototype两个库 function $(){ var elements=[]; for(var i=0;i<arguments.length;i++){ var element=arguments[i]; if(typeof element === "string"){ element=document.getElementById(element); } if(arguments.length === 1){ return element; } elements.push(element); } return elements; } var yahoo={}; yahoo.get=function(el){ if(typeof el==="string"){ return document.getElementById(el); } if(el instanceof Array){ var elements=[]; for(var i=;i<el.length;i++){ elements.push(yahoo.get(el[i])); } return elements; } if(el){ return el; } return null; }; yahoo.get=yuiToPrototypeAdapter; function yuiToPrototypeAdapter(){ if(arguments.length === 1){ return $.apply(window,e instanceof Array?e:[e]); }else{ return $.apply(window,arguments);//或者用call也行 } } window.onload=function(){ //var domarr=$("inp1","inp2"); prototype风格写法 //var domarr=yahoo.get(["inp1"]); YUI风格写法 //适配器模式的目的就是要让以下prototype的参数风格传入yahoo框架也可以使用 yahoo.get("inp1","inp2"); };
9、装饰者模式
//为对象添加新特性的技术 //实现同样的接口 //需要有子类 var CarInterface=new Interface("CarInterface",["getPrice","assemble"]); function Car(car){ //就是为了让子类继承的 让子类多一个父类的引用 this.car=car; //检查接口 Interface.ensureImplements(this,CarInterface); } Car.prototype={ constructor:Car, getPrice:function(){ return 200000; }, assemble:function(){ alert("组装汽车"); } }; //如果新的需求需要创建子类,而子类有可能会影响到父类的某些属性 //这时就要用装饰者模式 //用来把原始对象包装在具有同样接口的另一个对象中 function LightDecorator(car){//参数car代表原始对象 //LightDecorator.superClass是父类的原型对象 Car.call(this,car); } extend(LightDecorator,Car); LightDecorator.prototype={ constructor:LightDecorator, getPrice:function(){ return this.car.getPrice()+10000;//在子类上修改,没有影响到父类 }, assemble:function(){ alert("组装汽车"); } }; function IceBoxDecorator(car){ LightDecorator.call(this,car); } extend(IceBoxDecorator,Car); IceBoxDecorator.prototype={ constructor:IceBoxDecorator, getPrice:function(){ return this.car.getPrice()+20000;//在子类上修改,没有影响到父类 }, assemble:function(){ alert("组装汽车"); } }; var car=new Car(); alert(car.getPrice()); car=new LightDecorator(car); alert(car.getPrice()); car=new IceBoxDecorator(car); alert(car.getPrice()); ================================================= //装饰者模式不仅可以用在类上,还可以用在函数上 function getDate(){ return new Date().toString(); } //包装函数 function upperCaseDecorator(fn){ return function(){ return fn.apply(this,arguments).toUpperCase(); }; }
10、享元模式
//出厂商 出厂日期 拥有者 车牌号 登记日期 function Car(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={ constructor:Car, getMake:function(){ return this.make; }, getModel:function(){ return this.model; }, getYear:function(){ return this.year; }, renewRegistration:function(newRenewDate){ this.renewDate=newRenewDate; } }; var arr=[]; var start=new Date().getTime(); for(var i=0;i<5000000;i++){ arr.push(new Car("aaa","bbb","2012","ccc","112233","2013")); } var end=new Date().getTime(); alert(end-start);//700多毫秒 //占用内存大约 570M左右 //我们接下来用享元模式优化它 //内在数据:可以共享的属性 //外在数据:不能共享的属性 //享元模式要把内在数据抽出 function Car(make,model,year){ this.make=make; this.model=model; this.year=year; } Car.prototype={ constructor:Car, getMake:function(){ return this.make; }, getModel:function(){ return this.model; }, getYear:function(){ return this.year; } }; //工厂模式 利用闭包工厂把静态的东西初始化出来 var CarFactory=(function(){ //已经生产好的car var createdCars={}; return { createCar:function(make,model,year){ //如果createdCars对象里已经存在当前的make model year if(createdCars[make+model+year]){ return createdCars[make+model+year]; }else{ var car=new Car(make,model,year); createCars[make+model+year]=car; return 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, renewDate:renewDate, car:car }; }, renewRegistration:function(tag,newRenewDate){ carRecordDataBase[tag].renewDate=newRenewDate; } }; })(); var arr=[]; var start=new Date().getTime(); for(var i=0;i<5000000;i++){ arr.push(CarRecordManager.addCarRecord("aaa","bbb","2012","ccc","112233","2013")); } var end=new Date().getTime(); alert(end-start);//3000-5000多毫秒 //内存大约占用290M左右 //时间和空间不可兼得
//模拟web日历 并利用享元模式优化 var CalendarInterface=new Interface("CalendarInterface",["display"]); function CalendarYear(year,parent){ this.year=year; this.element=document.createElement("div"); this.element.style.display="none"; parent.appendChild(this.element); this.months=[]; this.numDays=[31,isLeapYear(this.year)?29:28,31,30,31,30,31,31,30,31,30,31]; for(var i=0;i<12;i++){ this.months[i]=new CalendarMonth(i,this.numDays[i],this.element); } } CalendarYear.prototype={ constructor:CalendarYear, display:function(){ for(var i=0;i<this.months.length;i++){ this.months[i].display(); } this.element.style.display="block"; } }; function CalendarMonth(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;i<numDays;i++){ this.days[i]=new CalendarDay(i+1,this.element); } } CalendarMonth.prototype={ constructor:CalendarMonth, display:function(){ for(var i=0;i<this.days.length;i++){ this.days[i].display(); } this.element.style.display="block"; } }; function CalendarDay(date,parent){ this.date=date; this.element=document.createElement("div"); this.element.style.display="none"; parent.appendChild(this.element); } CalendarDay.prototype={ constructor:CalendarDay, display:function(){ this.element.style.display="block"; this.element.innerHTML=this.date; } }; function isLeapYear(y){ return (y>0) && !(y%4) && ((y%100) || !(y%400)); } window.onload=function(){ var mydiv=document.getElementById("mydiv"); var myyear=new CalendarYear(2014,mydiv); myyear.display(); //一年都要new出来365个对象,年多了性能就很差了 }; <div id="mydiv"></div> //享元模式优化上面的日历 主要优化天对象 function CalendarMonth(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;i<numDays;i++){ //只用这一个实例 this.days[i]=calendarDaySingleInstance; } } CalendarMonth.prototype={ constructor:CalendarMonth, display:function(){ for(var i=0;i<this.days.length;i++){ this.days[i].display(i+1,this.element); } this.element.style.display="block"; } }; function CalendarDay(){} CalendarDay.prototype={ constructor:CalendarDay, display:function(date,parent){ var element=document.createElement("div"); parent.appendChild(element); element.style.display="block"; element.innerHTML=date; } }; var calendarDaySingleInstance=new CalendarDay();