JS设计模式之模板模式

[#]什么是模板模式?

模板模式是:抽象父类定义了子类需要重写的相关方法。并且这些方法,仍然是通过父类方法调用的。

根据描述,父类提供了“模板”并决定是否调用,子类进行具体实现。
[图片上传中...(image.png-cd26e1-1621520608105-0)]

/* 饮料类,父类 */
class Beverage {
    constructor() {
        if (new.target === Beverage) {
            throw new Error('抽象类不能直接实例化!')
        }
    }
  
    /* 烧开水,共用方法 */
    boilWater() { console.log('水已经煮沸') }
    
    /* 冲泡饮料,抽象方法 */
    brewDrink() { throw new Error('抽象方法不能调用!') }
    
    /* 倒杯子里,共用方法 */
    pourCup() { console.log('倒进杯子里') }
    
    /* 加调味品,抽象方法 */
    addCondiment() { throw new Error('抽象方法不能调用!') }
    
    /* 制作流程,模板方法 */
    init() {
        this.boilWater()
        this.brewDrink()
        this.pourCup()
        this.addCondiment()
    }
}

/* 咖啡类,子类 */
class Coffee extends Beverage {
    constructor() { super() }
    
    /* 冲泡饮料,实现抽象方法 */
    brewDrink() { console.log('冲泡咖啡') }
    
    /* 加调味品,实现抽象方法 */
    addCondiment() { console.log('加点咖啡伴侣') }
}

const coffee = new Coffee()
coffee.init()

// 输出:水已经煮沸
// 输出:冲泡咖啡
// 输出:倒进杯子里
// 输出:加点咖啡伴侣

如果需要创建一个新的饮料,那么增加一个新的实例类,并实现父类中的抽象方法。如果不实现就去调用 init 方法即报错:

// 接上一段代码
/* 茶类,子类 */
class Tea extends Beverage {
    constructor() { super() }
    
    /* 冲泡饮料,实现抽象方法 */
    brewDrink() { console.log('冲泡茶') }
    
    /* 注意这里,没有实现加调味品抽象方法 */
}

const tea = new Tea()
tea.init()

// 输出:水已经煮沸
// 输出:冲泡茶
// 输出:倒进杯子里
// Error: 抽象方法不能调用!

模板方法模式的通用实现

根据上面的例子,我们可以提炼一下模板方法模式。饮料类可以被认为是父类(AbstractClass),父类中实现了模板方法(templateMethod),模板方法中抽象了操作的流程,共用的操作流程是普通方法,而非共用的可变方法是抽象方法,需要被子类(ConcreteClass)实现,或者说覆盖,子类在实例化后执行模板方法,就可以按照模板方法定义好的算法一步步执行。主要有下面几个概念:

AbstractClass :抽象父类,把一些共用的方法提取出来,把可变的方法作为抽象类,最重要的是把算法骨架抽象出来为模板方法;
templateMethod :模板方法,固定了希望执行的算法骨架;
ConcreteClass :子类,实现抽象父类中定义的抽象方法,调用继承的模板方法时,将执行模板方法中定义的算法流程;
下面用通用的方法实现,这里直接用 class 语法:

/* 抽象父类 */
class AbstractClass {
    constructor() {
        if (new.target === AbstractClass) {
            throw new Error('抽象类不能直接实例化!')
        }
    }
    
    /* 共用方法 */
    operate1() { console.log('operate1') }
    
    /* 抽象方法 */
    operate2() { throw new Error('抽象方法不能调用!') }
    
    /* 模板方法 */
    templateMethod() {
        this.operate1()
        this.operate2()
    }
}

/* 实例子类,继承抽象父类 */
class ConcreteClass extends AbstractClass {
    constructor() { super() }
    
    /* 覆盖抽象方法 operate2 */
    operate2() { console.log('operate2') }
}

const instance = new ConcreteClass()
instance.templateMethod()

// 输出:operate1
// 输出:operate2
模板方法模式的优点:

封装了不变部分,扩展可变部分, 把算法中不变的部分封装到父类中直接实现,而可变的部分由子类继承后再具体实现; 提取了公共代码部分,易于维护, 因为公共的方法被提取到了父类,那么如果我们需要修改算法中不变的步骤时,不需要到每- 一个子类中去修改,只要改一下对应父类即可;
行为被父类的模板方法固定, 子类实例只负责执行模板方法,具备可扩展性,符合开闭原则;
模板方法模式的缺点:增加了系统复杂度,主要是增加了的抽象类和类间联系,需要做好文档工作;

模板方法模式的使用场景

如果知道一个算法所需的关键步骤,而且很明确这些步骤的执行顺序,但是具体的实现是未知的、灵活的,那么这时候就可以使用模板方法模式来将算法步骤的框架抽象出来;
重要而复杂的算法,可以把核心算法逻辑设计为模板方法,周边相关细节功能由各个子类实现;
模板方法模式可以被用来将子类组件将自己的方法挂钩到高层组件中,也就是钩子,子类组件中的方法交出控制权,高层组件在模板方法中决定何时回调子类组件中的方法,类似的用法场景还有发布-订阅模式、回调函数;

模板方法与其他设计模式区别

模板方法模式与工厂模式
模板方法模式的实现可以使用工厂模式来获取所需的对象。

模板方法模式和抽象工厂模式比较类似,都是使用抽象类来提取公共部分,不一样的是:
抽象工厂模式 提取的是实例的功能结构;
模板方法模式 提取的是算法的骨架结构;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354