设计模式
设计模式
1. 设计模式分类
工厂方法模式1、抽象工厂模式2、单例模式3、建造者模式4、原型模式5。
适配器模式6、装饰器模式7、代理模式8、外观模式9、桥接模式10、组合模式11、享元模式12。
策略模式13、模板方法模式14、观察者模式15、迭代子模式16、责任链模式17、命令模式18、备忘录模式19、状态模式20、访问者模式21、中介模式22、解释器模式23。
2. 常用设计模式
2.1 测试页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>设计模式</title>
<style>
#div1{
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<!-- 用于测试单例设计模式 -->
<button id="print">打印日志</button><br>
<!-- 用于测试策略设计模式 -->
<input type="text" name="" id="input"><button id="btn">注册</button><br>
<!-- 用于测试观察者模式(发布订阅模式) -->
<div id="div1"></div>
<!-- 用于测试组合设计模式 -->
<ul id="divUl"></ul>
<!-- 用于测试备忘录设计模式 -->
<div>
<p id="tips" style="margin: 0;">*</p>
<input type="text" id="originator"><button id="save">保存</button><button id="back">返回上一步</button>
</div>
</body>
</html>
2.2 单例模式
2.2.1 作用
确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
2.2.2 代码展示
// 单例模式
function Foodl(){
// _instance 自定义属性
if (!Foodl._instance) {
Foodl._instance = {
name: '单例模式'
}
}
return Foodl._instance
}
var obj1 = new Foodl()
var obj2 = new Foodl()
console.log(obj1 == obj2); // true
2.2.3 案列:打印日志
function FoodlReg(){
if (!FoodlReg._instance) {
FoodlReg._instance = {
logs: [],
print: function(){ // 输出
var _this = this;
var timer = setInterval(function(){
var log = _this.logs.shift();
if (log) {
console.log(log);
}else{
clearInterval(timer)
}
},500)
},
add: function(log){ // 添加
this.logs.push(log)
}
}
}
return FoodlReg._instance;
}
var arr = ["单例模式打印第一个日志","单例模式打印第二个日志","单例模式打印第三个日志","单例模式打印第四个日志"];
for (let i = 0; i < arr.length; i++) {
var obj = new FoodlReg()
obj.add(arr[i])
}
document.getElementById("print").onclick = function(){
var obj = new FoodlReg()
obj.print()
}
2.3 工厂模式
2.3.1 简单工厂模式
2.3.1.1 作用
提供一个创建一系列相关对象的入口,统一了解对象的创建职责,创建对象时无需关心具体类。
2.3.1.2 代码展示
// 工厂模式
function FooFactory(){
this.name = "FooFactory"
}
function BarFactory(){
this.name = "BarFactory"
}
// 2.1 简单工厂模式
function FactorySimple(arg){
var result;
if (arg == 'foo') {
return result = new FooFactory()
}else if (arg == 'bar') {
return result = new BarFactory()
}
}
var obj = FactorySimple('bar')
console.log('简单工厂模式',obj.name); // BarFactory
2.3.2 工厂模式
2.3.2.1 作用
提供一个创建一系列相关对象的入口,统一了解对象的创建职责,创建对象时无需关心具体类。
区别:使用一个工厂类进行创建。
2.3.2.2 代码展示
// 工厂模式
function FooFactory(){
this.name = "FooFactory"
}
function BarFactory(){
this.name = "BarFactory"
}
function Factory(){}
Factory.prototype.create = function(arg){
var result;
if (arg == 'foo') {
return result = new FooFactory()
}else if (arg == 'bar') {
return result = new BarFactory()
}
}
var fact = new Factory();
var objFact = fact.create('foo');
console.log('工厂模式',objFact.name); // FooFactory
2.3.2.3 案列:实现ajax请求
function Get(url){}
Get.prototype.done = function(fn){
var xhr = new XMLHttpRequest();
xhr.open('GET');
xhr.onload = function(){
fn(xhr.responseText)
}
}
function Post(url){}
Post.prototype.done = function(fn){
var xhr = new XMLHttpRequest();
xhr.open('POST');
xhr.onload = function(){
fn(xhr.responseText)
}
}
function Jsonp(url){}
Post.prototype.done = function(fn){
var script = document.createElement('script');
var name = url.match(/callback=([^&]+)/)[1];
script.src = url;
document.body.appendChild(script);
window[name] = fn;
}
function ajax(url,method){
var result;
switch(method){
case 'get':
result = new Get(url);
break;
case 'post':
result = new Post(url);
break;
case 'jsonp':
result = new Jsonp(url);
break;
}
return result;
}
// 简易写法,用于明白工厂模式的原理
// var xhr = ajax('data.php','get');
// xhr.done(function(data){
// console.log(data);
// })
// var xhr = ajax('data.php?callback=hello','jsonp');
// xhr.done(function(data){
// console.log(data);
// })
2.4 外观模式(门面模式)
2.4.1 作用
定义了一个高层接口,把子类集合在一起,然后方便使用
2.4.2 代码展示
// 3.外观模式
function Search(){} // 搜索
// 把子类集合在一起,统一管理使用
Search.prototype.show = function(str){
this.removeOldData() // 移除旧数据
this.showNewData() // 展示新数据
this.upDataPage() // 处理分页数据
}
Search.prototype.removeOldData = function(){}
Search.prototype.showNewData = function(){}
Search.prototype.upDataPage = function(){}
var s = new Search()
s.show('苹果')
2.5 适配器模式
2.5.1 作用
将一个类的接口转换成客户希望的另一个接口,是的原本由于接口不兼容而不能一起工作的哪些类可以一起工作
2.5.2 代码展示
function Adapter(class1,method){
return class1.prototype[method]
}
function FooTwo(){
this.show = Adapter(FooThree,'show')
}
FooTwo.prototype.show = function(){
console.log("两项插头");
}
function FooThree(){}
FooThree.prototype.show = function(){
console.log("三项插头");
}
var objTwo = new FooTwo()
objTwo.show(); // 三项插头
2.5.3 案例:根据浏览器窗口大小展示不同数据
function showPage(){
var screen = window.innerWidth < 800 ? 'small' : 'larger';
if (screen == 'small') {
this.show = Adapter(showSmallPage,'show')
}
}
showPage.prototype.show = function(){
console.log("正常显示");
}
function showSmallPage(){}
showSmallPage.prototype.show = function(){
console.log("小屏幕显示");
}
window.onresize = function(){
var objPage = new showPage()
objPage.show()
}
2.6 策略模式
2.6.1 作用
把操作封装成一个个的策略,根据不同的类型映射到不同的策略
2.6.2 代码展示
function Tactful(){}
Tactful.prototype.show = function(obj){
obj.print()
}
function FooTactful(){}
FooTactful.prototype.print = function(){
console.log("Foo 的策略");
}
function BarTactful(){}
BarTactful.prototype.print = function(){
console.log("Bar 的策略");
}
var tactful = new Tactful()
tactful.show(new FooTactful()) // Foo 的策略
tactful.show(new BarTactful()) // Bar 的策略
2.6.2 案例:注册验证
var validatalist = {
notNull: function(value,bool){
return (value !== '') == bool;
},
maxLength: function(value,maxLen){
console.log(value, maxLen);
return value.length < maxLen;
},
minLength: function(value,minLen){
return value.length > minLen
}
}
function AddValidata(id,opts){
this.el = document.getElementById(id);
this.opts = opts;
}
AddValidata.prototype.isPass = function(){
for (var key in this.opts) {
if (!validatalist[key](this.el.value, this.opts[key])) {
return false;
}
}
return true;
}
var v1 = new AddValidata('input',{
notNull: true,
maxLength: 12,
minLength: 8
})
document.getElementById('btn').onclick = function(){
console.log(v1.isPass());
}
2.7 观察者模式(发布订阅模式)
2.7.1 作用
一个被观察者管理所有相依于它的观察者物件,并且在本身的状态改变时主动发出通知
eg:js中元素事件绑定其实就是一个观察者模式,绑定事件的元素就是被观察者,里边调用的所有函数都是观察者,触发事件其实就是在发布订阅
2.7.2 代码展示
// 观察者模式(发布订阅模式)
function Observer(){}
// 绑定事件 订阅者
/**
* obj 订阅对象
* events 事件
* fn 回调函数
*/
Observer.prototype.bind = function(obj,events,fn){
obj.listeners = obj.listeners || {}
obj.listeners[events] = obj.listeners[events] || []
obj.listeners[events].push(fn)
}
// 发布事件
Observer.prototype.fire = function(obj,events){
var args = Array.prototype.slice.call(arguments).slice(2);
for (let i = 0; i < obj.listeners[events].length; i++) {
obj.listeners[events][i].apply(obj,args)
}
}
var divObs = document.getElementById('div1');
var objObserver = new Observer();
objObserver.bind(divObs,'show',function(){
console.log("123");
})
objObserver.bind(divObs,'show',function(){
console.log("2345");
})
divObs.onclick = function(){
objObserver.fire(divObs,'show'); // 打印依次输出:123,2345
}
2.7.3 案例:改变div大小
// 观察者模式(发布订阅模式)
function Observer(){}
// 绑定事件 订阅者
/**
* obj 订阅对象
* events 事件
* fn 回调函数
*/
Observer.prototype.bind = function(obj,events,fn){
obj.listeners = obj.listeners || {}
obj.listeners[events] = obj.listeners[events] || []
obj.listeners[events].push(fn)
}
// 发布事件
Observer.prototype.fire = function(obj,events){
var args = Array.prototype.slice.call(arguments).slice(2);
for (let i = 0; i < obj.listeners[events].length; i++) {
obj.listeners[events][i].apply(obj,args)
}
}
var divObs = document.getElementById('div1');
// 观察者模式(发布订阅模式) 案列---观察div大小改变情况
function ScaleMethod(el){
this.el = el;
this.scale(); // 滑动
this.wheel(); // 滚轮
}
ScaleMethod.prototype.scale = function(){
var downX = 0;
var downY = 0;
var downW = 0;
var downH = 0;
var _this = this;
this.el.onmousedown = function(e){
downX = e.pageX;
downY = e.pageY;
downW = _this.el.offsetWidth;
downH = _this.el.offsetHeight;
document.onmousemove = function(e){
_this.el.style.width = e.pageX - downX + 'px';
_this.el.style.height = e.pageY - downY + 'px';
objObserver.fire(_this, 'scale', _this.el.offsetWidth, _this.el.offsetHeight)
}
document.onmouseup = function(e){
document.onmousemove = null;
document.onmouseup = null;
}
return false
}
}
ScaleMethod.prototype.wheel = function(){
var _this = this;
this.el.onmousewheel = function(e){
if (e.wheelDelta > 0) {
this.style.height = this.offsetHeight - 10 + 'px';
}else{
this.style.height = this.offsetHeight + 10 + 'px';
}
objObserver.fire(_this, 'scale', _this.el.offsetWidth, _this.el.offsetHeight)
}
}
var objObserver = new Observer();
var s1 = new ScaleMethod(divObs)
objObserver.bind(s1,'scale',function(w,h){
divObs.innerHTML = w + 'px,' + h + 'px';
})
2.8 装饰者模式
2.8.1 作用
动态给一个对象添加一些额外的职责,对结果进行进一步的装饰
2.8.2 代码展示
function DecorateFoo (){
this.name = 'hello'
}
function DecorateBar (name){
this.name = name + ' Javascript';
}
function DecorateBaz (name){
this.name = name + ' !!!';
}
var decorate1 = new DecorateFoo();
var decorate2 = new DecorateBar(decorate1.name);
var decorate3 = new DecorateBaz(decorate2.name);
console.log(decorate3.name); // hello Javascript !!!
2.8.3 案例:金额操作(添加千分符和类型)
function Moneys(number){
this.number = number;
}
Moneys.prototype.get = function(){
return this.number;
}
function Milles(num){
this.number = this.set(num)
}
Milles.prototype.set = function(num){
var reg = /(?=(?!\b)(\d{3})+(\.\d*)?$)/g;
return num.toFixed(2).replace(reg,',')
}
Milles.prototype.get = function(){
return this.number;
}
function Currency(number,type){
this.number = this.set(number,type)
}
Currency.prototype.set = function(number,type){
type = type || '¥';
return type + number;
}
Currency.prototype.get = function(){
return this.number;
}
function SetMoney(num, type){
var m,mills,curr;
if (type != undefined) {
m1 = new Moneys(num);
mills = new Milles(m1.get());
curr = new Currency(mills.get(),type);
return curr.get()
}else{
m1 = new Moneys(num);
mills = new Milles(m1.get());
curr = new Currency(mills.get());
return curr.get()
}
}
console.log(SetMoney(1234567890.145,'$')); // $1,234,567,890.14
console.log(SetMoney(78913678.899)); // ¥78,913,678.90
console.log(SetMoney(987423241.099,'$')); // $987,423,241.10
2.9 组合模式
2.9.1 作用
将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性
2.9.2 组合模式及其案列
function Compose(name){
this.name = name;
this.childs = []
}
Compose.prototype.addChild = function(node){
this.childs.push(node)
}
function ComposeChild(name){
this.name = name;
}
function ShowRoot(node,parent){
var li = document.createElement('li');
li.innerHTML = node.name
parent.appendChild(li)
if (!node.childs) {
return
}
for (let i = 0; i < node.childs.length; i++) {
var ulChild = document.createElement('ul');
li.appendChild(ulChild)
ShowRoot(node.childs[i],ulChild)
}
console.log(li);
}
var root = new Compose("组合模式 html")
var leafHead = new Compose("组合模式 head")
root.addChild(leafHead)
var leafBody = new Compose("组合模式 body")
root.addChild(leafBody)
var leafDiv1 = new Compose("组合模式 div1")
leafBody.addChild(leafDiv1)
var leafDiv2 = new Compose("组合模式 div2")
leafBody.addChild(leafDiv2)
var leafP = new ComposeChild("组合模式 p")
leafDiv1.addChild(leafP)
var leafSpan = new ComposeChild("组合模式 span")
leafDiv1.addChild(leafSpan)
var leafBtn = new ComposeChild("组合模式 button")
leafDiv1.addChild(leafBtn)
var leftTile = new ComposeChild("组合模式 title")
leafHead.addChild(leftTile)
var leafMeat = new ComposeChild("组合模式 meta")
leafHead.addChild(leafMeat)
var leafStyle = new ComposeChild("组合模式 style")
leafHead.addChild(leafStyle)
var divUl = document.getElementById('divUl')
ShowRoot(root,divUl)
// 点击具有子元素的元素时,进行展开或隐藏操作
divUl.onclick = function(e){
if (e.target.nodeName.toLowerCase() == 'li') {
var child = e.target.children;
for (let i = 0; i < child.length; i++) {
if (child[i].nodeName.toLowerCase() == 'ul' && !child[i].flag) {
child[i].style.display = "none";
child[i].flag = true;
}else if(child[i].nodeName.toLowerCase() == 'ul'){
child[i].style.display = "block";
child[i].flag = false;
}
}
}
}
2.9.2 案列效果图
2.10 备忘录模式
2.10.1 作用
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个形态,这样就可以将该对象恢复到原先保存的状态。
2.10.2 备忘录模式及其案列
function Originator(){
this.state = ''; // 状态
this.memento = new Memento(); // 备忘录
}
Originator.prototype.getState = function(){ // 获取状态
return this.state;
}
Originator.prototype.setState = function(state){ // 设置状态
this.state = state;
}
Originator.prototype.createMemento = function(){ // 创建备忘录
this.memento.setMemento(this.state)
}
Originator.prototype.restorMemento = function(){ // 还原备忘录
this.setState(this.memento.getMemento())
}
function Memento(){
this.state = [];
}
Memento.prototype.getMemento = function(){ // 获取备忘录
return this.state.pop() || '';
}
Memento.prototype.setMemento = function(state){ // 设置备忘录
this.state.push(state)
}
var o1 = new Originator()
o1.setState("状态1");
console.log("初始状态:"+o1.getState());
o1.createMemento();
o1.setState("状态2");
console.log("改变后状态:"+o1.getState());
o1.createMemento();
o1.setState("状态3");
console.log("改变后状态:"+o1.getState());
o1.restorMemento();
console.log("还原后状态:"+o1.getState());
o1.restorMemento();
console.log("还原后状态:"+o1.getState());
var tips = document.getElementById('tips');
var orig = document.getElementById('originator');
var save = document.getElementById('save');
var back = document.getElementById('back');
var org = new Originator()
save.onclick = function(){
tips.innerHTML = "已保存";
org.createMemento();
org.setState(orig.value);
}
back.onclick = function(){
org.restorMemento();
orig.value = org.getState();
}
orig.oninput = function(){
tips.innerHTML = "*";
}