模板方法模式

2022/4/9 设计模式 大约 3 分钟

定义一系列操作的骨架,简化后面类似操作的内容。定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

该模式的主要优点:

  • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

该模式的主要缺点:

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

# 示例一

编写导航组件,有的带消息提示,有的是竖着的,有的是横着的。

function baseNav() {/*基础类,此处定义基本骨架*/
};

baseNav.prototype.action = function (fn) {/*特异性的处理,留出一个回调等待具体实现*/
};

1
2
3
4
5
6

导航组件多种多样,可能后面还会新增类型,那么我们不妨写一个基础的组件类,然后具体的实现,延迟到具体的使用时。

# 示例二

项目有一系列弹窗,每个弹窗的行为,大小,文字都会不同

function basePop(word, size) {
    this.word = word;
    this.size = size;
    this.dom = null;
}

basePop.prototype.init = function () {
    var div = document.createElement('div');
    div.innerHTML = this.word;
    div.style.width = this.size.width + 'px';
    div.style.height = this.size.height + 'px';
    this.dom = div;
}
basePop.prototype.hidden = function () {
    //定义基础操作
    this.dom.style.display = 'none';
}
basePop.prototype.confirm = function () {
    //定义基础操作
    this.dom.style.display = 'none';
}

function ajaxPop(word, size) {
    basePop.call(this, word, size);
}

ajaxPop.prototype = new basePop();
var hidden = ajaxPop.prototype.hidden;
ajaxPop.prototype.hidden = function () {
    hidden.call(this);
    console.log(1);
}
var confirm = ajaxPop.prototype.confirm;
ajaxPop.prototype.confirm = function () {
    confirm.call(this);
    console.log(1);
}
var pop = new ajaxPop('sendmes', {width: 100, height: 300});
pop.init();
pop.confirm();

var axios = {
    get: function () {
        return Promise.resolve();
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 示例三

现在我们有一系列自己的算法,但是这个算法常在不同的地方需要增加一些不同的操作;

function counter(){
  this.beforeCounter=[];
  this.afterCounter=[];
}

//然后我们把具体的不同部分留到具体使用的时候去扩展
//所以我们定义两个方法来扩展
counter.prototype.addBefore=function(fn){
   this.beforeCounter.push(fn);
}
counter.prototype.addAfter=function(fn){
   this.afterCounter.push(fn);
}

//最终计算方法
counter.prototype.count=function(num){
   //结果边两
   var _resultnum=num;
   //算法队列数组组装
   var _arr=[baseCount];
   _arr=this.beforeCounter.concat(_arr);
   _arr=_arr.concat(this.afterCounter);
   //不同部分的相同算法骨架
   function baseCount(num){
     num+=4;
     num*=2;
     return num;
   }
   //循环执行算法队列
   while(_arr.length>0){
     _resultnum=_arr.shift()(_resultnum);
   }
   return _resultnum;
}
//使用
var countObject=new counter();
countObject.addBefore(function(num){
   num--;
   return num;
})
countObject.addAfter(function(num){
  num*=2;
  return num;
})
countObject.count(10);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
上次编辑于: 2023年7月4日 09:36