1、模式定义
模板方法模式,在父类中定义一组操作算法的骨架,而将算法的一些实现步骤延迟到子类中去实现,使得子类可以在不改变父类算法结构的同时,又可以重新定义算法中的某些实现逻辑。
模板方法模式,就是把多个模型抽象化归一,从中抽象提取出一个最基本的模板,然后让其它模块继承这个模板方法,即可实现并拓展出风格类似但又各具特色的子模块。
这如同蛋糕店中各式各样的蛋糕,这些蛋糕事实上都共用了一个相同的“模具”。
2、模板方法模式 举例
使用模板方法模式,实现一个弹框组件。如下是 Alert基类:
// 定义一个弹框基类
var Alert = function(data) {
if (!data) return;
// 弹框中主体内容
this.content = data.content;
this.panel = document.createElement('div');
this.panel.className = 'alert';
this.contentNode = document.createElement('p');
this.contentNode.innerHTML = this.content;
// 确认按钮
this.confirmBtn = document.createElement('span');
this.confirmBtn.className = 'alert-confirm';
this.confirmBtn.innerHTML = data.confirm || '确认';
// 关闭按钮
this.closeBtn = document.createElement('b');
this.closeBtn.className = 'alert-close';
// 弹框确认事件
this.handleConfirm = data.confirm || function() {
// default do something
};
// 弹框关闭事件
this.handleClose = data.close || function() {
// default do something
}
}
// 定义Alert基类的原型方法
Alert.prototype = {
// 初始化
init: function() {
// 组装弹框中的元素
this.panel.appendChild(this.contentNode);
this.panel.appendChild(this.confirmBtn);
this.panel.appendChild(this.closeBtn);
// 把弹框插入到页面中
document.body.appendChild(this.panel);
// 绑定事件
this.bindEvent();
// 显示弹框
this.show();
},
bindEvent: function() {
var that = this;
this.closeBtn.onclick = function () {
that.handleClose();
that.hide();
}
this.confirmBtn.onclick = function() {
that.handleConfirm();
that.hide();
}
},
show: function() {
this.panel.style.display = 'block';
},
hide: function() {
this.panel.style.display = 'none';
}
}
基于这个 Alert 模板,我们来扩展一个改变了按钮样式的 RightAlert 弹框类。扩展如下:
var RightAlert = function(data) {
Alert.call(this, data);
// 扩展确认按钮的样式
this.confirmBtn.className = this.confirmBtn.className + ' btn-right';
}
RightAlert.prototype = new Alert();
基于这个 Alert 模板,我们再扩展一个带有弹框标题的 TitleAlert 弹框,扩展如下:
var TitleAlert = function(data) {
Alert.call(this, data);
// 增加一个弹框标题
this.title = data.title;
this.titleNode = document.createElement('h3');
this.titleNode.innerHTML = this.title;
}
TitleAlert.prototype = new Alert();
// 扩展 Alert的 init方法
TitleAlert.prototype.init = function() {
// 向弹框面板中插入标题节点
this.panel.insertBefore(this.titleNode, this.panel.firstChild);
Alert.prototype.init.call(this);
}
继续扩展,基于带标题的弹框 TitleAlert 扩展一个带“取消”按钮的弹框。扩展如下:
var CancelAlert = function(data) {
TitleAlert.call(this, data);
// 增加一个“取消按钮”
this.cancelText = data.cancelText;
this.cancelBtn = document.createElement('span');
this.cancelBtn.innerHTML = this.cancelText || '取消';
}
CancelAlert.prototype = new Alert();
CancelAlert.prototype.init = function() {
TitleAlert.prototype.init.call(this);
this.panel.appendChild(this.cancelBtn);
}
CancelAlert.prototype.bindEvent = function() {
var that = this;
TitleAlert.prototype.bindEvent.call(this);
this.cancelBtn.onclick = function() {
that.handleClose();
that.hide();
}
}
如此使用模板方法模式实现弹框组件,不仅结构功能样式统一,而且便于日后升级迭代,还便于扩展出其它类型的弹框。如下创建一个CancelAlert的弹框:
new CancelAlert({
title: '弹框标题',
content: '弹框内容',
confirm: function {
console.log('确认');
},
close: function() {
console.log('取消');
}
}).init();
3、模板方法模式 再举例
下面使用模板方法模式,实现一个导航组件。Nav基类如下:
// 导航组件 基类
var Nav = function(data) {
this.item = '<a href="{#href#}" title="{#title#}">{#name#}</a>';
this.html = '';
for (var i=0; i<data.length; i++) {
this.html += formatString(this.item, data[i]);
}
return this.html;
}
// 工具方法:把模板字符串中的插值符号替换成实际的文本
function formatString(str, data) {
return str.replace(/\{#(\w+)#\}/g, function(match, key) {
return typeof data[key] === undefined ? '' : data[key];
});
}
基于Nav基类,扩展出一种带有数字角标的导航组件。实现如下:
var NumNav = function(data) {
// 添加数据角标装饰
var tpl = '<b>{#num#}</b>';
for (var i=data.length-1; i>= 0; i--) {
data[i].name += data[i].name + formatString(tpl, data[i]);
}
// 继承Nav基类,并返回字符串
return Nav.call(this, data);
}
基于Nav基类,扩展出一种带有网址的导航组件。实现如下:
var LinkNav = function (data) {
var tpl = '<span>{#link#}</span>';
for (var i=data.length-1; i>=0; i--) {
data[i].name += data[i].name + formatString(tpl, data[i]);
}
// 继承Nav基类,并返回字符串
return Nav.call(this, data);
}
下面以NumNav为例创建对象,测试一下:
var nav = document.getElementById('content');
var data = [
{
href: 'http://www.geekxia.cn',
title: '点击我查看更多信息!',
name: '动态',
num: '10'
},
{
href: 'http://www.geekxia.cn',
title: '点击我查看更多信息!',
name: '新闻',
num: '20'
},
{
href: 'http://www.geekxia.cn',
title: '点击我查看更多信息!',
name: '关于',
num: '30'
}
];
nav.innerHTML = NumNav(data);
4、小结
模板方法模式的核心在于对方法的重用,它将核心方法封装在基类中,让子类继承基类的方法,实现对基类方法的共享。这是一种行为的约束。
子类所继承到的方法也可以进一步扩展,这就要求对基类继承的方法进行重写。当然为了更好地实践,我们通常要控制这种扩展,这样才能让基类对子类有更稳健的束缚力。然而子类对自身私有行为的扩展还是很有必要的。
本章完 2019-02-28
网友评论