「高级干货」设计模式学习:模板设计模式—“猫抓老鼠”

507 阅读5分钟

知识概论:

这次就不写前言了,直接开开启学习大厂设计模式之路吧。 学习设计模式,还是得先了解学的这个模式有什么特点。请看如下:

设计模式的精髓(特点): 其精髓在于分离不变部分可变部分,从而达到代码复用和扩展性的提升。

下面我们将一个特性一个特性来解释模板设计模式吧! image.png

封装不变性:

将整个算法或流程中不变的部分封装起来,保证了基础逻辑的稳定性。

我们以猫抓老鼠为例,所以我们先定义猫对象:

image.png 那么如何体现该设计模式的封装不变性呢?其实很简单,我们的封装就是为了保证基础逻辑。对于猫而言,抓老鼠都有着相同的特性。

其步骤如下:

  • 第一步: 观察与等待
  • 第二步: 突然袭击
  • 第三步: 精准的捕获
  • 第四步: 玩耍或放弃

这样我们对猫的模板方法emplateMethod 函数定义了一个操作的框架或流程,这个框架是不变的,就如上面的步骤。这里面的前三步,我们认为所有的猫都有这样的特性,称为封装的不变性称为固定的流程控制结构,也就是所有实体对象都具有的相同特点。第四步有个“或”是因为它体现了模板设计的其他特点 --> 也就是它的可拓展性,提供了可拓展点。

image.png

四个方法实现如下图所示: 代码均会在后面给出,小伙伴不要担心! image.png

延迟变化:

通过将易变的行为抽象为接口或抽象方法,并由子类实现,延迟了具体的实现细节到运行时决定。

这种特性体现在不同的实体类上,抽象对象AbstractCat 对于抽象猫有一个具体的抓鼠流程, 但是对于不同的猫类在最后有不同的做法,比如说接下来的:黑猫类,贪玩,抓到老鼠后就是一顿戏耍。,而对于白猫:贪吃,抓到了老鼠直接吃了。 所以这些独有的方法,都抽象为接口或抽象方法,并由子类实现。简单的说就是让 new AbstractCat( )的对象去实现。

就像这样: image.png 这一段代码就是说,对创建的新BCat黑猫对象原型重写stepFour函数,给他添加特殊的函数功能。

代码解释:

BCat.prototype = new AbstractCat()  //然后实例对象继承抽象对象
BCat.prototype.constructor = BCat; // 修复构造函数指向

image.png

查资料显示:修复构造函数的指向很重要,因为constructor属性常用于识别对象是由哪个构造函数创建的,这对于调试、反射或者某些基于类的库和框架来说非常关键。此外,一些设计模式或框架在运行时可能会依赖于准确的构造函数引用来进行特定的操作。作为初学阶段,还是不讨论这个,了解就好。 image.png 而延迟变化的特性就是想说,我们在确定步骤四到底是戏耍老鼠还是吃掉老鼠这一步骤,需要我们确定好了是什么猫的时候才能去选择的,如果使用的是黑猫BCat的话就是戏耍,白猫就是吃掉。

image.png

提高复用,简化扩展:

模板方法定义的框架可以被多个子类复用,减少了代码重复。新增不同的实现时,只需要添加新的子类并实现必要的抽象方法,而不需要改动模板方法本身,这极大地简化了系统的扩展过程。在创建白猫时像黑猫一样,让白猫类对象继承抽象猫就行,直接这样: image.png image.png 好了,整体代码就在下面,拿去用吧!

// "抽象类",使用构造函数和原型链来模拟
function AbstractCat() {   //构造函数,实际上是一个抽象对象
//相当于茶,猫等,没有具体指明是什么茶、什么猫,so 茶和猫都属于是抽象的对象
}

AbstractCat.prototype.templateMethod = function() {//在原型上定义方法,让其他的具体对象共享
    this.stepOne();  //观察与等待
    this.stepTwo();//突然袭击
    this.stepThree();//精准的捕获
    this.stepFour(); //玩耍或放弃
};

// 具体方法,在"抽象类"中定义,所有子类共享
AbstractCat.prototype.stepOne = function() {
    console.log("观察与等待");
};
AbstractCat.prototype.stepTwo = function() {
    console.log("突然袭击");
};
AbstractCat.prototype.stepThree = function() {
    console.log("精准的捕获");
};
AbstractCat.prototype.stepFour= function() {
    // throw new Error("玩耍或放弃,需要看是什么猫")
    console.log("玩耍或放弃,需要看是什么猫");
};
var BCat = function(type){  //创建黑猫类
    this.type = type
    console.log('这是一只'+type)
}
BCat.prototype = new AbstractCat()  //然后实例对象继承抽象对象
BCat.prototype.constructor = BCat; // 修复构造函数指向
BCat.prototype.stepFour = function(){ //给实例添加特殊函数,相当于重写了抽象对象的步骤四
    console.log("玩耍");
}

var WCat = function(type){  //创建白猫类
    this.type = type
    console.log('这是一只'+type)
}
WCat.prototype = new AbstractCat()  //然后实例对象继承抽象对象
WCat.prototype.constructor = WCat; // 修复构造函数指向
WCat.prototype.stepFour = function(){ //给白猫添加独特的函数,重写了抽象对象的步骤四
    console.log("放弃");
}

// 使用
var BlackCat = new BCat('黑猫') //黑猫有黑猫的特性,贪玩,所以会抓老鼠玩耍
BlackCat.templateMethod()
console.log('---------------分界线------------------')
var WhiteCat = new WCat('白猫')
WhiteCat.templateMethod()

总结

通过这样的设计,模板设计模式的精髓就能够体现出来了:固定了抓老鼠的基本流程(模板方法),同时允许子类灵活地改变或实现变化的部分,既保证了代码的复用性,又提供了足够的扩展性。,希望我们通过猫抓老鼠的例子对模板设计模式有了一点印象,可能涉及的不是很深,但对于初学者的我们,也许是个不错的方法呢。

写的不是很好,有错的地方希望小伙伴们多多包涵,俺也是新手,手下留情hhhh.