当前位置: 首页 > java >正文

java 设计模式之模板方法模式

简介

模板方法模式:定义一个算法的基本流程,将一些步骤延迟到子类中实现。模板方法模式可以提高代码的复用性,

模板方法中包含的角色:

  • 抽象类:负责给出一个算法的基本流程,它由一个模板方法和若干个基本方法构成
    • 模板方法:定义了算法的基本流程
    • 基本方法:模板方法的具体步骤,基本方法是可以被抽象到父类中的公共方法,多个子类可以同时使用的方法就是基本方法
    • 抽象方法:需要交给子类实现的方法,不同子类有着不同的实现
  • 具体子类:实现抽象类中定义的抽象方法

适用场景:适合于算法的整体步骤是固定的,但是其中个别部分容易变化,不同子类有着不同实现。

模板方法模式的实现

案例:一个小demo,厨师炒菜,炒菜的顺序是不变的,不同厨师使用不同的菜

第一步:抽象类,在模板方法中定义整体流程

public abstract class AbstractTemplate {// 模板方法:厨师炒菜的基本流程public void cookProcess(){pourOil(); // 倒油heatOil(); // 热油pourVegetable();  // 放菜pourSauce();   // 放调料fry();  // 炒菜}// 基本方法private void pourOil(){System.out.println("倒油");}private void heatOil(){System.out.println("热油");}private void fry() {System.out.println("炒菜");}// 交给子类实现的抽象方法protected abstract void pourVegetable();protected abstract void pourSauce();
}

第二步:具体子类实现父类中的抽象方法

// 具体子类:厨师A
public class ConcreteClassBaoCai extends AbstractTemplate {private String name;public ConcreteClassBaoCai() { }public ConcreteClassBaoCai(String name) {this.name = name;}@Overridepublic void pourVegetable() {System.out.println(name + ": 下锅的蔬菜是包菜");}@Overridepublic void pourSauce() {System.out.println(name + ": 下锅的调料是辣椒");}
}// 具体子类:厨师B
public class ConcreteClassCaiXin extends AbstractTemplate {private String name;public ConcreteClassCaiXin() { }public ConcreteClassCaiXin(String name) {this.name = name;}@Overridepublic void pourVegetable() {System.out.println(name + ": 下锅的蔬菜是菜心");}@Overridepublic void pourSauce() {System.out.println(name + ": 下锅的调料是醋");}
}

测试:

public class Client {public static void main(String[] args) {ConcreteClassBaoCai classBaoCai = new ConcreteClassBaoCai("厨师A");classBaoCai.cookProcess();ConcreteClassCaiXin classCaiXin = new ConcreteClassCaiXin("厨师B");classCaiXin.cookProcess();}
}

使用案例

jdk中的juc包,里面就使用到了模板方法设计模式。具体来说,juc包中提供了一系列的锁工具,无论是什么类型的锁,它的共有流程都是获取锁成功后改变锁标志状态然后向下执行、获取锁失败后进入阻塞队列、锁释放后唤醒阻塞队列中的头结点,不同类型的锁,例如公平锁、非公平锁,它们是获取锁的方式不同,例如,公平锁如果发现阻塞队列中有值,会加入阻塞队列,非公平锁会直接获取锁,获取失败后才会加入阻塞队列,所以juc把锁相关的共有流程提取到了aqs中,把不同类型的锁独有的特性放到了具体的锁实现类中。接下来做一个详细介绍

1、juc中的模板方法:aqs中定义了获取锁的整体流程

public abstract class AbstractQueuedSynchronizerextends AbstractOwnableSynchronizerimplements java.io.Serializable {// 这就是一个模板方法,定义了获取锁的流程public final void acquire(int arg) {if (!tryAcquire(arg) &&  // 获取锁,成功后返回true,然后向下执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 获取锁失败,进入阻塞队列selfInterrupt();}// 交给子类实现的抽象方法,定义了获取锁的方式protected boolean tryAcquire(int arg) {// 这里的设计值得借鉴,在空方法中抛异常,避免外部误调用throw new UnsupportedOperationException();}// 父类中实现的具体方法,定义了获取锁失败后进入进入阻塞队列的方式,// 无论哪种类型的锁,进入阻塞队列的方式都是一样的final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}
}

2、子类的具体实现

实现方式1:ReentrantLock中公平锁的实现方式

static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}// 这里就是重写了上面父类中的tryAquire方法,定义了公平锁获取锁的方式protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {  // 锁没有被获取到if (!hasQueuedPredecessors() &&  // 队列中是否有元素compareAndSetState(0, acquires)) {  // 如果没有,获取锁setExclusiveOwnerThread(current);  // 获取锁成功后,当前线程设置为独占线程return true;}}else if (current == getExclusiveOwnerThread()) {  // 如果当前线程已经获取到了锁int nextc = c + acquires;  // 可重入的逻辑if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
}

实现方式2:ReentrantLock中非公平锁的实现方式

static final class NonfairSync extends Sync {// 这里重写了父类中的tryAcquire方法,定义了非公平锁的获取方式protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {if (compareAndSetState(0, acquires)) {  // 如果锁没有被持有,直接尝试获取锁setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
}

总结:这里顺便介绍了公平锁和非公平锁的实现,可以看出,唯一的区别在于,非公平锁在会直接尝试获取锁,公平锁会先判断阻塞队列中有没有元素,如果没有,再去获取锁,否则加入阻塞队列。

总结

模板方法设计模式,它的使用场景是,如果有多个类似的实体,它们有着共同的流程,每个实体又都有自己独特的地方,可以把它们相同的功能抽取出来,设计成一个模板方法,然后子类负责实现某些关键步骤,例如,之前的公平锁和非公平锁,它们获取锁的方式不同,但是获取锁失败后进入阻塞队列的方式是一样的。

http://www.xdnf.cn/news/100.html

相关文章:

  • OpenVINO怎么用
  • 树莓派超全系列教程文档--(32)config.txt常用音频配置
  • 焊接机器人的设计
  • 【医学影像 AI】早产儿视网膜病变国际分类(第三版)
  • 区块链如何成为智能城市的底层引擎?从数据透明到自动化治理
  • docker 安装prometheus普罗米修斯
  • WPF常用技巧汇总
  • FPGA-VGA
  • 2、SpringAI接入ChatGPT与微服务整合
  • Muduo网络库实现 [十六] - HttpServer模块
  • CentOS 7 linux系统从无到有部署项目
  • yarn的三大组件及各自的作用
  • 互联网大厂Java面试:微服务与分布式系统挑战
  • SpringAI入门:对话机器人
  • 初识Redis · C++客户端string
  • 360蜘蛛IP完整版,360搜索引擎蜘蛛IP列表.pdf
  • 第24周:Resnet结合DenseNet
  • PHP使用pandoc把markdown文件转为word
  • Manim教程:第七章 坐标系统
  • Docker安装hoppscotch
  • 【“星瑞” O6 评测】—NPU 部署 face parser 模型
  • mysql数据库基础
  • 从零构建 Vue3 登录页:结合 Vant 组件与 Axios 实现完整登录功能
  • 010301-cdn_waf-web扩展1-基础入门-网络安全
  • 云计算(Cloud Computing)概述——从AWS开始
  • 设计模式之工厂方法模式
  • 文件编码检测与转换:从自定义实现到 ICU 应用
  • Linux网络通信核心机制解析与层级架构探秘
  • 华为OD机试真题——统计匹配的二元组个数(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
  • list的使用